Automating On-Premise Tabular Model refresh with Azure DataLake Gen1 Connection - oauth-2.0

We are using Azure Data Lake Gen1 as source in our Tabular model. We have deployed this model on on-prem server. Now, every time we have to manually refresh the credentials to process the model. We want to automate that. I have already tried following link.
https://blog.gbrueckl.at/2017/11/processing-azure-analysis-services-oauth-sources-like-azure-data-lake-store/
But gives following error
credentials provided cannot be used for the DataLake source
This is the credentials object that is getting created from script:
{
"AuthenticationKind": "OAuth2",
"kind": "DataLake",
"path": "https://mydatalake.azuredatalakestore.net/",
"Expires": "Thu, 21 Mar 2020 01:37:50 GMT",
"AccessToken": "",
"RefreshToken": ""
}
Code:
[string] $ClientID = ""
[string] $AASServerName = ""
[string] $AASDatabaseName = ""
[string] $CredentialName =""
[string] $Password = ""
Import-Module SqlServer
$authUrl = "https://login.microsoftonline.com/{tenantid}/oauth2/v2.0/token"
$body = #{
"client_id" = $ClientID;
"scope" = "openid offline_access";
"grant_type" = "password";
"username" = $CredentialName
"password" = $Password
}
Write-Output "Getting Authentication-Token ..."
$adlsToken = Invoke-RestMethod -Uri $authUrl –Method POST -Body $body
$dtformat = "r"
$dateTime = Get-Date -f $dtformat
$newdateTime=[datetime]::ParseExact($datetime,$dtformat,$Null).AddSeconds($adlsToken.expires_in).ToString($dtformat)
#modify the token so it can be used within our ADLS Datasource
$adlsToken | Add-Member -NotePropertyName "AuthenticationKind" "OAuth2"
$adlsToken | Add-Member -NotePropertyName "kind" "DataLake"
$adlsToken | Add-Member -NotePropertyName "path" "https://mydatalake.azuredatalakestore.net/"
$adlsToken | Add-Member -NotePropertyName "Expires" $newdateTime
$adlsToken | Add-Member -NotePropertyName "AccessToken" $adlsToken.access_token
$adlsToken | Add-Member -NotePropertyName "RefreshToken" $adlsToken.refresh_token
$adlsToken = $adlsToken | Select-Object -Property * -ExcludeProperty scope,expires_in,ext_expires_in,token_type, id_token,refresh_token,access_token,token_type
Write-Output "Updating DataSource with latest token ..."
#Update the DataSource with a valid Token
$updateDataSource = '{
"createOrReplace": {
"object": {
"database": "'+$AASDatabaseName+'",
"dataSource": "DataLake/https://mydatalake azuredatalakestore net/xyx/data"
},
"dataSource": {
"type": "structured",
"name": "DataLake/https://mydatalake azuredatalakestore net/xyx/data",
"connectionDetails": {
"protocol": "data-lake-store",
"address": {
"url": "https://mydatalake.azuredatalakestore.net"
}
},
"credential": ' + (ConvertTo-Json $adlsToken) + '
}
}
}'
$updateDataSource
Invoke-ASCmd -Server $AASServerName -Database $AASDatabaseName -Query $updateDataSource
Write-Output "Start Processing <dataOnly> ..."
$processObject = '
{
"refresh": {
"type": "dataOnly",
"objects": [ {"database": "' + $AASDatabaseName + '"} ]
}
}'
Invoke-ASCmd -Server $AASServerName -Database $AASDatabaseName -Query $processObject
Write-Output "Finished!"

Related

Unable to Export All Channels Conversations from a Team using Graph-PowerShell

I'm using the below script to export channel conversations. It works fine per channel. But I want to be able to export all channel conversations in a Team. I tried using ForEach ($Channel in $Channels) but it's not working. Can someone help please. Credit to PSGuy for the original script: https://www.psguy.eu/how-to-export-ms-teams-chat-to-html-file-for-backup/
[CmdletBinding(DefaultParameterSetName='default')]
param
(
[Parameter(ParameterSetName='Channel')]
$Team,
[Parameter(Mandatory=$false,ParameterSetName='default')]
[Parameter(Mandatory=$true,ParameterSetName='Channel')]
$Channel
)
Write-Host "Exporting Team Chats Homie"
$scriptpath = $MyInvocation.MyCommand.Path
$dir = Split-Path $scriptpath
$Date = Get-Date -Format "MM-dd-yyyy-HHmm"
$clientId = "YourClientID"
$tenantName = "YourTenantName"
$clientSecret = "YourClientSecret"
$resource = "https://graph.microsoft.com/"
$ReqTokenBody = #{
Grant_Type = "Password"
client_Id = $clientID
Client_Secret = $clientSecret
Username = 'YourTeamsAdmUserName'
Password = 'YourTeamsAdmPassword'
Scope = "https://graph.microsoft.com/.default"
}
$TokenResponse = Invoke-RestMethod -Uri "https://login.microsoftonline.com/$TenantName/oauth2/v2.0/token" -Method POST -Body $ReqTokenBody
#Getting all Groups
$apiUrl = "https://graph.microsoft.com/beta/groups"
$Data = Invoke-RestMethod -Headers #{Authorization = "Bearer $($TokenResponse.access_token)"} -Uri $apiUrl -Method Get -ErrorVariable RespErr
$Groups = ($Data | Select-Object Value).Value
if ($Team -eq $NULL){
Write-Host "You have" -NoNewline
Write-Host " $($Groups.Count)" -ForegroundColor Yellow -NoNewline
Write-Host " teams."
Write-Host ""
Write-Host "Messages from which Team do you want to export to the HTML format?" -ForegroundColor Yellow
$Groups | FT DisplayName,Description
$Team = Read-Host "Type one of the Team (DisplayName)"
}
$TeamID = ($Groups | Where-Object {$_.displayname -eq "$($Team)"}).id
$apiUrl = "https://graph.microsoft.com/v1.0/teams/$TeamID/Channels"
$Data = Invoke-RestMethod -Headers #{Authorization = "Bearer $($TokenResponse.access_token)"} -Uri $apiUrl -Method Get
if ($Channel -eq $NULL){
Write-Host "You choose" -NoNewline
Write-Host " $($Team)" -ForegroundColor Yellow -NoNewline
Write-Host " Team."
Write-Host ""
$Channels = ($Data | Select-Object Value).Value
Write-Host "Messages from which Channel do you want to export to the HTML format?" -ForegroundColor Yellow
$Channels | FT DisplayName,Description
$Channel = Read-Host "Type one of the Channel(DisplayName)"
}
$ChannelID = (($Data | Select-Object Value).Value | Where-Object {$_.displayName -eq "$($Channel)"}).ID
$apiUrl = "https://graph.microsoft.com/beta/groups/$TeamID/members"
$Data = Invoke-RestMethod -Headers #{Authorization = "Bearer $($TokenResponse.access_token)"} -Uri $apiUrl -Method Get
class messageData
{
[string]$dateTime
[string]$from
[string]$body
messageData()
{
$this.dateTime = ""
$this.from = ""
$this.body = ""
}
}
function parseMessage($Data) #returns resultset
{
$messages = ($Data | Select-Object Value).Value
foreach ($message in $Messages)
{
$messageID = $message.id
$messageSet = New-Object System.Collections.ArrayList;
$result = New-object messageData
#parse message
if ($NULL -eq $message.from.user.displayName) {
$result.dateTime = $message.createdDateTime
$result.from = $message.from.application.displayName
}
else {
$result.dateTime = $message.createdDateTime
$result.from = $message.from.user.displayName
}
$bodyOut = ""
if ($NULL -eq $message.summary)
{
foreach ($attachment in $message.attachments)
{
$output = $attachment.content
$output = $output.substring(14)
$output = $output.substring(0,$output.length-4)
$bodyOut = $bodyOut + $output
}
}
else {
$bodyOut = $message.summary;
}
$bodyOut = $bodyOut + $message.body.content
$result.body = $bodyOut;
$messageSet.Add($result)
#parse replies
$repliesURI = "https://graph.microsoft.com/beta/teams/" + $TeamID + "/channels/" + $ChannelID + "/messages/" + $messageID + "/replies?`$top100"
$repliesResponse = Invoke-RestMethod -Method Get -Uri $repliesURI -Headers #{Authorization = "Bearer $($TokenResponse.access_token)"}
foreach ($reply in $repliesResponse.value )
{
$replyData = New-Object messageData
if ($NULL -eq $reply.from.user.displayName) {
$replyData.dateTime = $reply.createdDateTime
$replyData.from = $reply.from.application.displayName
}
else {
$replyData.dateTime = $reply.createdDateTime
$replyData.from = $reply.from.user.displayName
}
$bodyOut = ""
if ($NULL -eq $message.summary)
{
foreach ($attachment in $reply.attachments)
{
$output = $attachment.content
$output = $output.substring(14)
$output = $output.substring(0,$output.length-4)
$bodyOut = $bodyOut + $output
}
}
else {
$bodyOut = $message.summary
}
$replyData.body = $bodyOut + $reply.body.content
$messageSet.Add($replyData)
}
$resultList.Add($messageSet)
}
return
}
$TokenResponse = Invoke-RestMethod -Uri "https://login.microsoftonline.com/$TenantName/oauth2/v2.0/token" -Method POST -Body $ReqTokenBody
$resultList = New-Object System.Collections.ArrayList;
$apiUrl = "https://graph.microsoft.com/beta/teams/$TeamID/channels/$ChannelID/messages?`$top=100"
$sourceData = Invoke-RestMethod -Headers #{Authorization = "Bearer $($TokenResponse.access_token)"} -Uri $apiUrl -Method Get
parseMessage($sourceData)
$nextLink = $sourceData.'#Odata.NextLink'
while ($NULL -ne $nextLink)
{
$nextURL = $nextLink;
$sourceData = Invoke-RestMethod -Headers #{Authorization = "Bearer $($TokenResponse.access_token)"} -Uri $nextURL -Method Get
parseMessage($sourceData)
$nextLink = $sourceData.'#Odata.NextLink'
}
$resultFieldSet = New-Object System.Collections.ArrayList
foreach($resultData in $resultList) {
$resultFields = $resultData | Select-Object #{Name = 'DateTime'; Expression = {Get-Date -Date (($_).dateTime) -Format 'MM/dd/yyyy hh:mm:ss.fff tt'}}, #{Name = 'From'; Expression = {((($_).from))}}, #{Name = 'Message'; Expression = {(($_).body) -replace '<.*?>',''}}| Sort-Object DateTime
$resultFieldSet.Add($resultFields)
}
$Header = #"
<style>
h1, h5, th { text-align: center; }
table { margin: auto; font-family: Segoe UI; box-shadow: 10px 10px 5px #888; border: thin ridge grey; }
th { background: #0046c3; color: #fff; max-width: 400px; padding: 5px 10px; }
td { font-size: 11px; padding: 5px 20px; color: #000; }
tr { background: #b8d1f3; }
tr:nth-child(even) { background: #dae5f4; }
tr:nth-child(odd) { background: #b8d1f3; }
</style>
"#
$count = 0
foreach ($resultCount in $resultList){
$count = $count + $resultCount.Count
}
$body = "<body><b>Generated:</b> $(Get-Date -Format 'MM/dd/yyyy hh:mm tt') <br><br> <b>Team Name:</b> $($Team) <br> <b>Channel Name:</b> $($Channel) <br><br>" + "<b>number of messages:</b> " + $count + " <br><br>"
$body = $body + "</head>"
$resultHtml = ""
foreach ($resultFields in $resultFieldSet){
$tempHtml = $resultFields | ConvertTo-Html -Head $header
$resultHtml = $tempHtml + "<br>" + $resultHtml
}
$resultHtml = $body + "<br>" + $resultHtml
$Export = "$dir\TeamsHistory\$Team-$Channel"
New-Item -ItemType Directory -Path $Export -ErrorAction Ignore
$resultHtml | Out-File $Export\$Team-$Channel-$Date.html
Write-Host "
"
Write-Host "Messages from the" -NoNewline
Write-Host " $($Team)" -NoNewline -ForegroundColor Yellow
Write-Host " team and" -NoNewline
Write-Host " $($Channel)" -NoNewline -ForegroundColor Yellow
Write-Host " channel were generated and saved to the" -NoNewline
Write-Host " $($Export)" -NoNewline -ForegroundColor Yellow
Write-Host " as a" -NoNewline
Write-Host " $($Team)-$($Channel)-$($Date).html" -NoNewline -ForegroundColor Yellow
Write-Host " file."
Write-Host "
"
To export messages from all Channels within a team, you need to loop through the Channels.
Could you please refer below code:
[CmdletBinding(DefaultParameterSetName='default')]
param
(
[Parameter(ParameterSetName='Channel')]
$Team,
[Parameter(Mandatory=$false,ParameterSetName='default')]
[Parameter(Mandatory=$true,ParameterSetName='Channel')]
$Channel
)
Write-Host "Exporting Team Chats Homie"
$scriptpath = $MyInvocation.MyCommand.Path
$dir = Split-Path $scriptpath
$Date = Get-Date -Format "MM-dd-yyyy-HHmm"
#TODO: Update for your tenant
$clientId = "XXXXXXX"
$tenantName = "YYYYYY"
$clientSecret = "ZZZZZZZ"
$resource = "https://graph.microsoft.com/"
class messageData
{
[string]$dateTime
[string]$from
[string]$body
messageData()
{
$this.dateTime = ""
$this.from = ""
$this.body = ""
}
}
function parseMessage($Data) #returns resultset
{
$messages = ($Data | Select-Object Value).Value
foreach ($message in $Messages)
{
$messageID = $message.id
$messageSet = New-Object System.Collections.ArrayList;
$result = New-object messageData
#parse message
if ($NULL -eq $message.from.user.displayName) {
$result.dateTime = $message.createdDateTime
$result.from = $message.from.application.displayName
}
else {
$result.dateTime = $message.createdDateTime
$result.from = $message.from.user.displayName
}
$bodyOut = ""
if ($NULL -eq $message.summary)
{
foreach ($attachment in $message.attachments)
{
$output = $attachment.content
if ($output)
{
$output = $output.substring(14)
$output = $output.substring(0,$output.length-4)
}
$bodyOut = $bodyOut + $output
}
}
else {
$bodyOut = $message.summary;
}
$bodyOut = $bodyOut + $message.body.content
$result.body = $bodyOut;
$messageSet.Add($result)
#parse replies
$repliesURI = "https://graph.microsoft.com/beta/teams/" + $TeamID + "/channels/" + $ChannelID + "/messages/" + $messageID + "/replies?`$top100"
$repliesResponse = Invoke-RestMethod -Method Get -Uri $repliesURI -Headers #{Authorization = "Bearer $($TokenResponse.access_token)"}
foreach ($reply in $repliesResponse.value )
{
$replyData = New-Object messageData
if ($NULL -eq $reply.from.user.displayName) {
$replyData.dateTime = $reply.createdDateTime
$replyData.from = $reply.from.application.displayName
}
else {
$replyData.dateTime = $reply.createdDateTime
$replyData.from = $reply.from.user.displayName
}
$bodyOut = ""
if ($NULL -eq $message.summary)
{
foreach ($attachment in $reply.attachments)
{
$output = $attachment.content
if ($output)
{
$output = $output.substring(14)
$output = $output.substring(0,$output.length-4)
}
$bodyOut = $bodyOut + $output
}
}
else {
$bodyOut = $message.summary
}
$replyData.body = $bodyOut + $reply.body.content
$messageSet.Add($replyData)
}
$resultList.Add($messageSet)
}
return
}
$ReqTokenBody = #{
Grant_Type = "Password"
client_Id = $clientID
Client_Secret = $clientSecret
#TODO: Update for your tenant
Username = 'admin#XXXXXXXX.onmicrosoft.com'
Password = 'YYYYYYYYYY'
Scope = "https://graph.microsoft.com/.default"
}
$TokenResponse = Invoke-RestMethod -Uri "https://login.microsoftonline.com/$TenantName/oauth2/v2.0/token" -Method POST -Body $ReqTokenBody
#Getting all Groups
$apiUrl = "https://graph.microsoft.com/beta/groups"
$Data = Invoke-RestMethod -Headers #{Authorization = "Bearer $($TokenResponse.access_token)"} -Uri $apiUrl -Method Get -ErrorVariable RespErr
$Groups = ($Data | Select-Object Value).Value
if ($Team -eq $NULL){
Write-Host "You have" -NoNewline
Write-Host " $($Groups.Count)" -ForegroundColor Yellow -NoNewline
Write-Host " teams."
Write-Host ""
Write-Host "Messages from which Team do you want to export to the HTML format?" -ForegroundColor Yellow
$Groups | FT DisplayName,Description
$Team = Read-Host "Type one of the Team (DisplayName)"
}
$TeamID = ($Groups | Where-Object {$_.displayname -eq "$($Team)"}).id
$apiUrl = "https://graph.microsoft.com/v1.0/teams/$TeamID/Channels"
$Data = Invoke-RestMethod -Headers #{Authorization = "Bearer $($TokenResponse.access_token)"} -Uri $apiUrl -Method Get
if ($Channel -eq $NULL){
Write-Host "You choose" -NoNewline
Write-Host " $($Team)" -ForegroundColor Yellow -NoNewline
Write-Host " Team."
Write-Host ""
$Channels = ($Data | Select-Object Value).Value
#Write-Host "Messages from which Channel do you want to export to the HTML format?" -ForegroundColor Yellow
#$Channels | FT DisplayName,Description
#$Channel = Read-Host "Type one of the Channel(DisplayName)"
}
foreach ($Channel in $Channels)
{
#$ChannelID = (($Data | Select-Object Value).Value | Where-Object {$_.displayName -eq "$($Channel)"}).ID
$ChannelID = $Channel.id
Write-Host "Channel ID: " + $ChannelID
#$apiUrl = "https://graph.microsoft.com/beta/groups/$TeamID/members"
$apiUrl = "https://graph.microsoft.com/v1.0/groups/$TeamID/members"
$Data = Invoke-RestMethod -Headers #{Authorization = "Bearer $($TokenResponse.access_token)"} -Uri $apiUrl -Method Get
$TokenResponse = Invoke-RestMethod -Uri "https://login.microsoftonline.com/$TenantName/oauth2/v2.0/token" -Method POST -Body $ReqTokenBody
$resultList = New-Object System.Collections.ArrayList;
#Correct URL with escape
$apiUrl = "https://graph.microsoft.com/beta/teams/$TeamID/channels/$ChannelID/messages?`$top=100"
Write-Host $apiUrl
$sourceData = Invoke-RestMethod -Headers #{Authorization = "Bearer $($TokenResponse.access_token)"} -Uri $apiUrl -Method Get
parseMessage($sourceData)
$nextLink = $sourceData.'#Odata.NextLink'
while ($NULL -ne $nextLink)
{
$nextURL = $nextLink;
$sourceData = Invoke-RestMethod -Headers #{Authorization = "Bearer $($TokenResponse.access_token)"} -Uri $nextURL -Method Get
parseMessage($sourceData)
$nextLink = $sourceData.'#Odata.NextLink'
}
$resultFieldSet = New-Object System.Collections.ArrayList
foreach($resultData in $resultList) {
$resultFields = $resultData | Select-Object #{Name = 'DateTime'; Expression = {Get-Date -Date (($_).dateTime) -Format 'MM/dd/yyyy hh:mm:ss.fff tt'}}, #{Name = 'From'; Expression = {((($_).from))}}, #{Name = 'Message'; Expression = {(($_).body) -replace '<.*?>',''}}| Sort-Object DateTime
$resultFieldSet.Add($resultFields)
}
$Header = #"
<style>
h1, h5, th { text-align: center; }
table { margin: auto; font-family: Segoe UI; box-shadow: 10px 10px 5px #888; border: thin ridge grey; }
th { background: #0046c3; color: #fff; max-width: 400px; padding: 5px 10px; }
td { font-size: 11px; padding: 5px 20px; color: #000; }
tr { background: #b8d1f3; }
tr:nth-child(even) { background: #dae5f4; }
tr:nth-child(odd) { background: #b8d1f3; }
</style>
"#
$count = 0
foreach ($resultCount in $resultList){
$count = $count + $resultCount.Count
}
$body = "<body><b>Generated:</b> $(Get-Date -Format 'MM/dd/yyyy hh:mm tt') <br><br> <b>Team Name:</b> $($Team) <br> <b>Channel Name:</b> $($Channel) <br><br>" + "<b>number of messages:</b> " + $count + " <br><br>"
$body = $body + "</head>"
$resultHtml = ""
foreach ($resultFields in $resultFieldSet){
$tempHtml = $resultFields | ConvertTo-Html -Head $header
$resultHtml = $tempHtml + "<br>" + $resultHtml
}
$resultHtml = $body + "<br>" + $resultHtml
$channelDisplayName = $Channel.displayName
$Export = "$dir\TeamsHistory\$Team-$channelDisplayName"
New-Item -ItemType Directory -Path $Export -ErrorAction Ignore
$resultHtml | Out-File $Export\$Team-$channelDisplayName-$Date.html
Write-Host "
"
Write-Host "Messages from the" -NoNewline
Write-Host " $($Team)" -NoNewline -ForegroundColor Yellow
Write-Host " team and" -NoNewline
Write-Host " $($channelDisplayName)" -NoNewline -ForegroundColor Yellow
Write-Host " channel were generated and saved to the" -NoNewline
Write-Host " $($Export)" -NoNewline -ForegroundColor Yellow
Write-Host " as a" -NoNewline
Write-Host " $($Team)-$($channelDisplayName)-$($Date).html" -NoNewline -ForegroundColor Yellow
Write-Host " file."
Write-Host ""
}#end foreach ($Channel in $Channels)

Keycloak how to obtain RPT

I'm trying to fetch all the permissions from Keycloak, ie all resources and scopes that a user has access to.
Basically, I want to fetch an RPT from Keycloak, with permissions shown as on Keycloak REST API docs and the below image
Unfortunately, the docs are either confusing, or the way of Requesting a RPT isn't shown. This example is all under RPT, and moving on, the docs just explain how to further introspect the token.
How can you obtain this token (anything that contains the permissions like in the sample token actually) from Keycloak?
You may want to try something like this:
USER=test
PASS=test
CLIENT_ID=test
CLIENT_SECRET=your-client-secret
RESULT=`curl -s --data "grant_type=password&client_id=${CLIENT}&client_secret=${CLIENT_SECRET}&username=${USER}&password=${PASS}" http://localhost:8080/realms/master/protocol/openid-connect/token`
ACCESS_TOKEN=`echo $RESULT | jq -r .access_token`
RPT_RESULT=`curl -s -H "Authorization: Bearer ${ACCESS_TOKEN}" --data "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket&client_id=${CLIENT}&audience=${CLIENT}" http://localhost:8080/realms/master/protocol/openid-connect/token`
echo $RPT_RESULT | jq -r .access_token | cut -d "." -f2 | base64 -d | jq
This retrieves an access token first and then queries an RPT.
This should give you an output like this:
{
"exp": 1643134734,
"iat": 1643134674,
"jti": "f60caba8-8f20-43f0-9054-6389f998032c",
"iss": "http://localhost:8080/realms/master",
"aud": "test",
"sub": "18cce3e6-e3a0-4be9-a1ff-6635adf5928b",
"typ": "Bearer",
"azp": "test",
"session_state": "539a81bf-aa27-4ce4-911a-405f5a2c90ac",
"acr": "1",
"realm_access": {
"roles": [
"create-realm",
"default-roles-master",
"offline_access",
"admin",
"uma_authorization"
]
},
"resource_access": {
"master-realm": {
"roles": [
"view-identity-providers",
"view-realm",
"manage-identity-providers",
"impersonation",
"create-client",
"manage-users",
"query-realms",
"view-authorization",
"query-clients",
"query-users",
"manage-events",
"manage-realm",
"view-events",
"view-users",
"view-clients",
"manage-authorization",
"manage-clients",
"query-groups"
]
},
"account": {
"roles": [
"manage-account",
"manage-account-links",
"view-profile"
]
},
"test-realm-realm": {
"roles": [
"view-identity-providers",
"view-realm",
"manage-identity-providers",
"impersonation",
"create-client",
"manage-users",
"query-realms",
"view-authorization",
"query-clients",
"query-users",
"manage-events",
"manage-realm",
"view-events",
"view-users",
"view-clients",
"manage-authorization",
"manage-clients",
"query-groups"
]
}
},
"authorization": {
"permissions": [
{
"rsid": "9f708183-5aa3-4a8a-96fd-5be9aef5427d",
"rsname": "Default Resource"
}
]
},
"scope": "profile email",
"sid": "539a81bf-aa27-4ce4-911a-405f5a2c90ac",
"email_verified": false,
"preferred_username": "admin"
}

Using Microsoft Graph to update Applications passwordCredentials using addpassword method

I'm changing my Powershell script that updates an applications passwordCredentials (specifically the keyId,secretText and Hint) as previously we used PATCH but this has now changed as per : https://developer.microsoft.com/en-us/graph/blogs/breaking-changes-application-and-serviceprincipal-api-updates-in-microsoft-graph-beta/
However when I use POST I cannot seem to update the KeyId, secretText and Hint anymore, this continues to work with PATCH but was looking to update my code before this goes live, can you assist?
This Code still works at present :
$jsonData = '
{
"passwordCredentials": [
{
"customKeyIdentifier": null,
"endDateTime": "2119-05-01T10:18:33.4995826Z",
"keyId": "'+ $($guid) +'",
"startDateTime": "2019-05-01T10:18:33.4995826Z",
"secretText": "'+ $($Password.Password) +'",
"hint": "'+ $($Passwordhint) +'"
}
]
}'
#Specify the URI to call and method
$uri = "https://graph.microsoft.com/beta/applications/$ID/"
$method = "PATCH"
Invoke-WebRequest -Method $method -Uri $uri -ContentType "application/json" -Body $jsonData -Headers #{Authorization = "$($global:authtoken.authorization)"} -ErrorAction Stop | Out-Null
The following does POST but creates a password with none of the information provided, passwordCredentials are not updated for the application as expected:
#Populate JSON data for the application
$jsonData = '
{
"passwordCredentials": [
{
"customKeyIdentifier": null,
"endDateTime": "2119-05-01T10:18:33.4995826Z",
"keyId": "'+ $($guid) +'",
"startDateTime": "2019-05-01T10:18:33.4995826Z",
"secretText": "'+ $($Password.Password) +'",
"hint": "'+ $($Passwordhint) +'"
}
]
}'
#Specify the URI to call and method
$uri = "https://graph.microsoft.com/beta/applications/$ID/addPassword"
$method = "POST"
Invoke-WebRequest -Method $method -Uri $uri -ContentType "application/json" -Body $jsonData -Headers #{Authorization = "$($global:authtoken.authorization)"} -ErrorAction Stop | Out-Null
It appears the addPassword action from https://graph.microsoft.com/beta/$metadata has a wrapped passwordCredential as a parameter:
...
<Action Name="addPassword" IsBound="true">
<Parameter Name="bindingParameter" Type="microsoft.graph.application" Nullable="false"/>
<Parameter Name="passwordCredential" Type="microsoft.graph.passwordCredential"/>
<ReturnType Type="microsoft.graph.passwordCredential" Nullable="false"/>
</Action>
...
A POST body like this:
{
"passwordCredential": {
"customKeyIdentifier": null,
"endDateTime": "2119-05-01T10:18:33.4995826Z",
"startDateTime": "2019-05-01T10:18:33.4995826Z",
"displayName": "new test password"
}
}
should return a generated credential like:
{
"#odata.context": "https://graph.microsoft.com/beta/$metadata#microsoft.graph.passwordCredential",
"customKeyIdentifier": null,
"endDateTime": "2119-05-01T10:18:33.4995826Z",
"keyId": "...",
"startDateTime": "2019-05-01T10:18:33.4995826Z",
"secretText": "...",
"hint": "...",
"displayName": "new test password"
}

Create Release JSON body works in Postman but not in PowerShell script

Using the Azure DevOps REST API and PowerShell, I am trying to create a very basic Release pipeline. My JSON body works fine in Postman, but errors out when run with Invoke-RestMethod in PowerShell.
I'm following the documentation from https://learn.microsoft.com/en-us/rest/api/azure/devops/build/definitions/create?view=azure-devops-rest-5.0.
Using Postman, I've created a JSON body that works perfectly and repeatedly (provided I either change the release pipeline name or delete the previously created one). I've copied the JSON content verbatim to my PowerShell script, setting the variable $requestBody equal to the JSON content. When I run the script, I get an error (see below for error content).
Following is my test script (apologize for the length, but I thought it important I include the entire JSON.
$organization = "ORGNAME"
$token = "6-OBFUSCATED_TOKEN-a"
$project = "PROJECTNAME"
# Base64-encode the Personal Access Token (PAT) appropriately
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $user, $token)))
$uri = "https://dev.azure.com/$($organization)/$($project)/_apis/build/definitions?api-version=5.0"
$requestBody = '{
"source": "restAPI",
"revision": 1,
"description": null,
"name": "RepoName-CD",
"path": "\\",
"projectReference": null,
"properties": {},
"environments": [
{
"name": "Stage 1",
"variables": {},
"variableGroups": [],
"preDeployApprovals": {
"approvals": [
{
"rank": 1,
"isAutomated": true,
"isNotificationOn": false
}
],
"approvalOptions": {
"requiredApproverCount": null,
"releaseCreatorCanBeApprover": false,
"autoTriggeredAndPreviousEnvironmentApprovedCanBeSkipped": false,
"enforceIdentityRevalidation": false,
"timeoutInMinutes": 0,
"executionOrder": "beforeGates"
}
},
"postDeployApprovals": {
"approvals": [
{
"rank": 1,
"isAutomated": true,
"isNotificationOn": false
}
],
"approvalOptions": {
"requiredApproverCount": null,
"releaseCreatorCanBeApprover": false,
"autoTriggeredAndPreviousEnvironmentApprovedCanBeSkipped": false,
"enforceIdentityRevalidation": false,
"timeoutInMinutes": 0,
"executionOrder": "afterSuccessfulGates"
}
},
"deployPhases": [
{
"deploymentInput": {
"parallelExecution": {
"parallelExecutionType": "none"
},
"skipArtifactsDownload": false,
"artifactsDownloadInput": {
"downloadInputs": []
},
"demands": [],
"enableAccessToken": false,
"timeoutInMinutes": 0,
"jobCancelTimeoutInMinutes": 1,
"condition": "succeeded()",
"overrideInputs": {}
},
"rank": 1,
"phaseType": "agentBasedDeployment",
"name": "Agent job",
"refName": null,
"workflowTasks": []
}
],
"environmentOptions": {
"emailNotificationType": "OnlyOnFailure",
"emailRecipients": "release.environment.owner;release.creator",
"skipArtifactsDownload": false,
"timeoutInMinutes": 0,
"enableAccessToken": false,
"publishDeploymentStatus": true,
"badgeEnabled": false,
"autoLinkWorkItems": false,
"pullRequestDeploymentEnabled": false
},
"executionPolicy": {
"concurrencyCount": 1,
"queueDepthCount": 0
},
"schedules": [],
"retentionPolicy": {
"daysToKeep": 30,
"releasesToKeep": 3,
"retainBuild": true
},
"processParameters": {},
"properties": {},
"environmentTriggers": []
}
]
}'
Invoke-RestMethod -Uri $uri -Method POST -ContentType "application/json" -Headers #{Authorization = ("Basic {0}" -f $base64AuthInfo) } -Body $requestBody
When I run the script, I expected to get back the JSON confirming that the release pipeline was created, but instead I get the following error from PowerShell.
Invoke-RestMethod : {"$id":"1","innerException":null,"message":"Value cannot be null.\r\nParameter name: definition.Repository","typeName":"System.ArgumentNullException,
mscorlib","typeKey":"ArgumentNullException","errorCode":0,"eventId":0}
At C:\GitHub\landingzone\AzureDevOpsApiDev\testmule.ps1:113 char:1
+ Invoke-RestMethod -Uri $uri -Method POST -ContentType "application/js ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
Any assistance or guidance will be greatly appreciated.
It looks like you're making a POST to the create build definition API. If you're trying to create a release definition, you probably need to post to a URL like this
POST https://vsrm.dev.azure.com/{organization}/{project}/_apis/release/definitions?api-version=5.0
Well, the lesson here is to not copy and paste code from a previously working script WITHOUT DOUBLE CHECKING IT.
Mea Culpa.

cURL to Guzzle conversion

I have a cURL that works well:
curl -H "Authorization: Token 71e24088d13304cc11f5a0fa93f2a2356fc43f41" -H "Content-Type: application/json" -X POST -d
'{
"reviewer": {"name": "test name", "email": "test#email.com"},
"publication": {"title": "test title", "doi": "xxx"},
"complete_date": {"month": 6, "year": 2015}
}'
https://my.url.com/ --insecure
And would like to use the multi curl from Symfony2 Guzzle
What I tried so far:
$client = new \Guzzle\Http\Client();
$request = $client->post('https://my.url.com/');
$jsonBody = "'{ ";
$jsonBody .= '"reviewer": {"name": "'.$review['name'].'", "email":"'.$review['email'].'"}, ';
$jsonBody .= '"publication": {"title": "';
if ($review['article_title'] != '')
$jsonBody .= $review['article_title'];
else
$toExec .= $review['manuscript_title'];
if ($review['doi'] != '')
$jsonBody .= '", "doi": "'.$review['doi'].'"}, ';
else
$toExec .= '"}, ';
$jsonBody .= '"complete_date": {"month": '.$month.', "year": '.$year.'}';
$jsonBody .= "}' ";
$options = [
'headers' => [ 'Content-Type' => 'application/json', 'Authorization' => 'Token 71e24088d13304cc11f5a0fa93f2a2356fc43f41' ],
'body' => $jsonBody,
];
$request->getCurlOptions()->set(CURLOPT_SSL_VERIFYHOST, false);
$request->getCurlOptions()->set(CURLOPT_SSL_VERIFYPEER, false);
$response = $client->post('-d', $options);
But this doesn't work, I am getting an array as a result which I am not supposed to.
You might try moving your authorization to cURL.
$token = '71e24088d13304cc11f5a0fa93f2a2356fc43f41';
$request->getCurlOptions()->set(CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
$request->getCurlOptions()->set(CURLOPT_USERPWD, "$token:X");
X is the password but often there is none needed so you just add an X (random character)

Resources