Check Groupmembership or jobtitle of Logged On User withouth calling azuread (Get-AzureADUserMembership) - powershell

I am building a script with a colleague. This script will show a popup.
We want this popup only to be shown to a certain group of users (teachers and not students). So we want to check group membership of a user. Since this script will be installed on the computer locally and we would not like to install azuread cmdlets on all our computers and also not pass azure ad credentials to the script.
So we where wondering if their is azure-ad info about group membership stored on the computers locally. And also if we can acces it via powershell? (if we could get jobtitle of user that would also be helpfull)
Other ideas are also welcome.

The information related user that you are looking for less likely to be stored locally.
So to meet your requirement, you will have to have some means to communicate with the Azure AD. Your requirement was not to make use of any additional libraries and by using PowerShell.
Graph API can come handy.
To prevent user intervention, you can use App only Graph permission for the App.
You can refer this article to know more App registration, client secret & Application permission :
https://learn.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app
https://learn.microsoft.com/en-us/graph/notifications-integration-app-registration
You consume the graph endpoint from PowerShell and acquire the necessary details of the logged user from the Azure AD without a necessity of external libraries.
#Acquiring the graph token
$web="https://login.microsoftonline.com/<TENANT ID>/oauth2/v2.0/token"
$body = "client_id=<CLIENT ID>&scope=https://graph.microsoft.com/.default&client_secret=<CLIENTSECRET>&grant_type=client_credentials"
$response = Invoke-WebRequest $web -Body $body -Method Post
$token = ($response | ConvertFrom-Json).access_token
#Getting the logged username that will be used in the graph api
$upn = $env:USERNAME + "#" + $env:USERDNSDOMAIN
#building the authorization header
$header = #{"Authorization" = " Bearer $token"}
#gettting the user details
$content = Invoke-WebRequest "https://graph.microsoft.com/v1.0/users/$upn" -Headers $header -Method Get
$details = $content.Content | ConvertFrom-Json
#getting the group membership
$content = Invoke-WebRequest "https://graph.microsoft.com/v1.0/users/$upn/memberof" -Headers $header -Method Get
$groupdetails = ($content.Content | ConvertFrom-Json).value

Related

Cannot call Graph API using delegated permissions in PowerShell

I have an Azure App Registration setup as below:
Not shown: two redirect URIs for https://login.microsoftonline.com/common/oauth2/nativeclient (which I believe is needed for auth code flows if using PowerShell?) and https://MYDEVSERVER
We have a web API that can correctly access Graph against this App Registration by using a hybrid on-behalf-of flow and IConfidentialClientApplication, along with a bootstrap token provided by an Office add-in.
The creation of this App Registration is actually done by a PowerShell script, and I want to add a feature that will try to login as the script user to make a test Graph API call (against the /me endpoint) to make sure it is setup correctly. The script can ensure that the App Registration is granted admin consent for the Graph API permissions so the script user does not have to provide consent.
From what I understand, the authorization code flow can be used to test these delegated permissions. However, I've tried two approaches with different results as below using the MSAL module to get the token and then use that token in the authorization header for a Graph call to simply return details about the logged in user via the /me endpoint:
If I don't pass a client secret to the Get-MsalToken cmdlet, I do get a login prompt but Get-MsalToken always returns the error 'AADSTS7000218: The request body must contain the following parameter: 'client_assertion' or 'client_secret'
If I do provide a client_secret, I do NOT get the login prompt and can successfully get a token, but the Graph call fails saying that the /me request is only valid with delegated authentication flow. Which makes sense because we need a user context.
So I'm stuck - I can't use the user context with the interactive login without it expecting a client secret that if I do use is using a different flow that's not supporting delegated permissions. What am I doing wrong? Or do I need to use a completely different authentication flow?
Does the fact that the App Registration is designed to handle SSO with Office web add-ins have anything to do with it? Meaning, I have a scope (e.g. api://MYWEBSERVER/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/access_as_user, as used by the add-in manifests for purposes of issuing a bootstrap token from the Office JS library to our Web API) and a list of authorized client applications (Office apps) in the 'Expose an API' section. I'm wondering if only those applications are authorized, and not the PowerShell script...
I've also seen some suggestions about using the device code flow and I'm wondering if I'm missing a step to get a refresh token (as demonstrated here but I can't get that example to work either using a client secret (I can't figure out the proper Get-MsalToken parameter combination).
$graphEndPointUrl = "https://graph.microsoft.com/v1.0/me"
$connectionDetails = #{
'TenantId' = $tenantId #Set variable to your own value
'ClientId' = $clientId #Set variable to your own value
'Scopes' = 'https://graph.microsoft.com/.default'
}
#Using the above information, it throws this error on Invoke-RestMethod:
#ERROR: "{"error":"invalid_client","error_description":"AADSTS7000218: The request body must contain the following parameter: 'client_assertion' or 'client_secret'.
#"error_codes":[7000218]
#"error_uri":"https://login.microsoftonline.com/error?code=7000218"}"
$connectionDetails = #{
'TenantId' = $tenantId #Set variable to your own value
'ClientId' = $clientId #Set variable to your own value
'ClientSecret' = $clientSecret | ConvertTo-SecureString -AsPlainText -Force #Set $clientSecret variable to your own value
'Scopes' = 'https://graph.microsoft.com/.default'
}
#Using the above information, it throws this error on Invoke-RestMethod:
#ERROR: "{"error":{"code":"BadRequest","message":"/me request is only valid with delegated authentication flow."
Import-Module MSAL.PS
try {
$myAccessToken = Get-MsalToken #connectionDetails
Write-Host $myAccessToken.AccessToken
$authHeader = #{
'Authorization' = $myAccessToken.CreateAuthorizationHeader()
}
$response = Invoke-RestMethod -Uri $graphEndPointUrl -Headers $authHeader
#Error thrown below
if ($null -eq $response) {
Write-Error "An unexpected error occurred making a test Graph API call to the $($graphEndPointUrl) endpoint" -ForegroundColor Red
}
else {
Write-Host "The test Graph API call to the $($graphEndPointUrl) endpoint was successful!"
}
}
catch {
$result = $_.Exception.Response.GetResponseStream()
$reader = New-Object System.IO.StreamReader($result)
$reader.BaseStream.Position = 0
$reader.DiscardBufferedData()
$responseBody = $reader.ReadToEnd();
$errorObj = ConvertFrom-Json -InputObject $responseBody
Write-Host "An unexpected error occurred making a test Graph API call to the $($graphEndPointUrl) endpoint: $($errorObj.error.message)"
}
Update
Everything you need I believe is written in this Microsoft article - https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow
I noticed a key difference in the tutorial you referenced and your setup, thats the ID Token that is used with the hybrid setup, you can read more about it in the MS guide, check response_mode .
So principal goes as follow > You get an authorization code > use that code to generate your access and refresh tokens > then you can use different API calls with the Access token in the header.
As for your code, here are the things that are missing or are not correctly implemented.
You are missing the RedirectUri param. Make sure the URL is exact I have just put https://localhost as an example.
$connectionDetails = #{
'TenantId' = $tenantId #Set variable to your own value
'ClientId' = $clientId #Set variable to your own value
'ClientSecret' = $clientSecret | ConvertTo-SecureString -AsPlainText -Force #Set $clientSecret variable to your own value
'Scopes' = 'https://graph.microsoft.com/.default'
'RedirectUri' = 'https://localhost'
}
Your header is not implementing the Authorization properly. Check out the example from the tutorial
$myAuthMethods = (Invoke-RestMethod -Headers #{Authorization = "Bearer $($myAccessToken.AccessToken)" } `
-Uri "https://graph.microsoft.com/beta/me/authentication/methods" `
-Method Get).value
$myAuthMethods
The header have the following structure, where the word Bearer is before the token.
$header = #{
"Authorization" = "Bearer $($myAccessToken.AccessToken)"
}
Make sure you have the access token $myAccessToken.AccessToken in the header.
At that point, you should at least get a different error if request does not proceed.
The issue is that an additional "Mobile and desktop applications" platform needs to be added to the App Registration on the Authentication page (with "https://login.microsoftonline.com/common/oauth2/nativeclient" as a Redirect URI), as it only has a web platform currently. Then Get-MsalToken works without a client Id:
$myAccessToken = Get-MsalToken -ClientId $clientID -TenantId $tenantId -Interactive -Scopes 'https://graph.microsoft.com/.default' -RedirectUri 'https://login.microsoftonline.com/common/oauth2/nativeclient'
However, an additional request is needed to get a refresh token and use that for the Graph call:
$myAccessToken = Get-MsalToken -ClientId $clientID -TenantId $tenantId -Silent

Run application insights query from powershell module

With the AZ Cli I can run the following to query application insight instances:
az monitor app-insights query
There doesn't appear to be a direct equivalent in the azure powershell module. I've seen suggestions that you should use the REST API for application insights, but that requires an API key.
I do not have an API key for each of my (many) application insights' and do not want to have to create and store them so I can query application insights - the reason for not being able to use the Az Cli in my script is I want to run my script as a function app and the az cli isn't supported in function apps.
Is there an alternative way to query AI from powershell that I'm missing?
At the moment, Azure PowerShell just provides the module to manage Azure application insight resource. For more details, please refer to here. So if you want to query application insight with PowerShell, we need to use rest API. Besides, if you do not want to access it with API key, you can do that with AD token. For more details, please refer to here
For example
If you want to Azure AD auth to access Azure application insights API, please refer to the following steps
Register Azure AD application in your tenant
Configure API permissions
Create a client secret for the application
Configure assign contributor to the AD application in your subscription
Script
$appKey=""
$appId=""
$resource="https://api5.applicationinsights.io"
$secpasswd = ConvertTo-SecureString $appKey -AsPlainText -Force
$mycreds = New-Object System.Management.Automation.PSCredential ($appId, $secpasswd)
Connect-AzAccount -ServicePrincipal -Tenant "hanxia.onmicrosoft.com" -Credential $mycreds
$res=Get-AzAccessToken -ResourceUrl $resource
$headers=#{"Authorization"="Bearer "+$res.Token}
$body=#{"timespan"="P7D"; "query"="requests| summarize totalCount=sum(itemCount) by bin(timestamp, 30m)"}| ConvertTo-Json
Invoke-RestMethod 'https://api.applicationinsights.io/v1/apps/bd7cacd8-9607-4b53-b57b-995255292f36/query' -Method 'POST' -Headers $headers -Body $body -ContentType "application/json"
Given you have $appInsResourceGroupName and $appInsName pointing to your Application Insights instance.
$component = Get-AzApplicationInsights -ResourceGroupName $appInsResourceGroupName -Name $appInsName
$apiKey = New-AzApplicationInsightsApiKey -ApplicationInsightsComponent $component -Permissions ReadTelemetry -Description "Collector"
$query = "requests | limit 5"
(Invoke-WebRequest -Method POST -Uri https://api.applicationinsights.io/v1/apps/$($component.AppId)/query -ContentType application/json -Body $('{"query":"' + $query + '"}') -Headers #{"X-Api-Key"=$apiKey.ApiKey}).Content
to clean up / remove unused API keys
Get-AzApplicationInsightsApiKey -ApplicationInsightsComponent $component | ?{$_.Description -eq "Collector"} | %{Remove-AzApplicationInsightsApiKey -ApplicationInsightsComponent $component -ApiKeyId $_.Id}
if you're using any domestic clouds you need to account for that; e.g. for China you need to change the URL to api.applicationinsights.azure.cn

connect to tfs with different user by using tfs api

I am trying to run a shellscript program to connect tfs by using tfsapi service.
Jenkins run on logged user but i want to connect tfs with different user (not with windows logged user)
How can i add credential to my powershell code? I use UseDefaultCredentials but need to use different user to connect tfs. how can i do that?
$tfsServerURL = "https://test.tfs.siemens.net/test"
$BuildDefinition = "test.rgs.project"
$URL = "$($tfsServerURL)"
#Get ID of Builddefinition
$buildDefinitionID = (Invoke-RestMethod -Uri ($URL + '/_apis/build/definitions?api-version=2.0&name=' + $BuildDefinition) -Method GET -UseDefaultCredentials).value.id
We can use this official Rest API: Definitions - List to get specific build definition with additional parameter name. And this API supports Oauth2 authentication. However, OAuth 2.0 is not supported on Azure DevOps Server. So we need to use PAT authentication with below least scope: vso.build.
Therefore, if you want to connect tfs with different user using this API, please provide their corresponding PAT, and then below script should work as expected.
$tfsServerURL = "https://test.tfs.siemens.net/test"
$BuildDefinition = "test.rgs.project"
$URL = "$($tfsServerURL)"
$connectionToken="PAT here"
$base64AuthInfo= [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($connectionToken)"))
$AzureDevOpsAuthenicationHeader = #{authorization = "Basic $base64AuthInfo"}
#Get ID of Builddefinition
$buildDefinitionID = (Invoke-RestMethod -Uri ($URL + '/_apis/build/definitions?api-version=5.0&name=' + $BuildDefinition) -Method GET -Headers $AzureDevOpsAuthenicationHeader).value[0].id
Write-host $buildDefinitionID

Permissions required for these TFS (on premise) REST urls?

I am trying to automate some jobs that require information about agent pools and agents and while the job script works just fine for regular users of my TFS collection it fails miserably for a service account.
My job script tries to access urls
http://<instance>/<collection>/_apis/distributedtask/pools
http://<instance>/<collection>/_apis/distributedtask/pools/<pool>/agents
Initially my service account got a response like
TF400813: The user '<service account>' is not authorized to access this resource
The service account was previously not member of any TFS related AD group but after creating a new group and adding it to 'Project Collection Valid Users' the call does not fail but the response does not include any pool information still.
If I modify the service account interactive logins the GUI for agents in the agent pool shows no information but the hint
no agents are registered or you do not have permission to view the agents
suggests that permission is missing.
I have tried to add the service account to various groups in TFS like Project Collection Administrators, Project Collection Build Administrators, etc. all to no avail.
So in short, what permissions does a service account need to retrieve information from the urls mentioned in the start?
I can reproduce this issue, it does not work even change the regular users as the service account.
So, as a workaround for now you can call the REST API using the regular users or PAT in your script.
Below PowerShell script works for me:
$user = "Domain\username"
$token = "password"
# Base64-encodes the Personal Access Token (PAT) appropriately
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $user,$token)))
$uri = "http://SERVER:8080/tfs/_apis/distributedtask/pools/1/agents/5"
$result = Invoke-RestMethod -Uri $uri -Method Get -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)}
Write-Host "result = $($result | ConvertTo-Json -Depth 100)"

How can I authenticate to AAD and call the Graph API as a Daemon Application with PowerShell?

I am trying to do some very quick tests on Azure Active Directory, and I want to use a Daemon Application to access the Graph API without needing a user present to authenticate. I want to verify that my application registration can successfully authenticate to AAD, that my client secret is valid, and make calls to the AAD Graph API.
I have registered a "Web App/API" in my directory already, and I have set it up to have the appropriate permissions to call the AAD Graph API in the App Only Context. I have also generated an application key/certificate for my app so that I can authenticate as a confidential client.
I want to take a look at my AAD Token, and the output from the Graph API after my call. How can I use PowerShell to quickly accomplish this?
This question is very similar to this one where create a PowerShell script to authenticate as a Native Client Application. However, in this situation, there are some subtle and important differences because you want to authenticate as a confidential client. Specifically, we need to create a Client Credential so that we can authenticate without a user as a Daemon Application.
First you need to download and save the .NET dlls for ADAL. The download link can be found on Nuget.
Note: We specifically use ADAL v2 here.
You can extract the contents of the .nupkg with a File Extractor like
7z, WinZip, etc...
Extract the contents from \lib\net45\ and copy them into your working directory. I put the files in their own "ADAL" folder, to keep it separate.
Then you should be able to create a new PowerShell script with the following:
# Load ADAL
Add-Type -Path ".\ADAL\Microsoft.IdentityModel.Clients.ActiveDirectory.dll"
# Output Token and Response from AAD Graph API
$accessToken = ".\Token.txt"
$output = ".\Output.json"
# Application and Tenant Configuration
$clientId = "<AppIDGUID>"
$tenantId = "<TenantID>"
$resourceId = "https://graph.windows.net"
$login = "https://login.microsoftonline.com"
# Create Client Credential Using App Key
$secret = "<AppKey>"
# Create Client Credential Using Certificate
#$certFile = "<PFXFilePath>"
#$certFilePassword = "<CertPassword>"
#$secret = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Certificate -ArgumentList $certFile,$certFilePassword
# Get an Access Token with ADAL
$clientCredential = New-Object Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential($clientId,$secret)
$authContext = New-Object Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext("{0}/{1}" -f $login,$tenantId)
$authenticationResult = $authContext.AcquireToken($resourceId, $clientcredential)
($token = $authenticationResult.AccessToken) | Out-File $accessToken
# Call the AAD Graph API
$headers = #{
"Authorization" = ("Bearer {0}" -f $token);
"Content-Type" = "application/json";
}
Invoke-RestMethod -Method Get -Uri ("{0}/{1}/users?api-version=1.6" -f $resourceId,$tenantId) -Headers $headers -OutFile $output
Note: You will need to update the App ID, Tenant ID, and your App Secret information in this script. If you use a certificate to authenticate, simply comment out the code that uses the App Key, and un-comment the code which uses the certificate. I have also pre-configured the AAD Graph API call to return the users in my tenant, but you can change this REST call to whatever you want.
After you successfully run the script, you should get 2 new files in your working directory: A text file that contains your encoded JSON access token, which can be base64 decoded on sites like this, and a JSON file with the response from the AAD Graph API.
Let me know if this helps!