I'm attempting to write a PowerShell script that will automate the process of adding new user accounts to our Jira instance. I've provided my code but I'm honestly not even getting to that point as I am receiving a 401 error:
This resource requires WebSudo.
I have seen these two posts on the Jira support forum but it's not clear to me how I could adapt the code to get and then apply it to my REST call. I would be fine with changing this to use the .Net WebClient class if that would make all of this easier, but right now I'm at a bit of a loss.
$url = "https://devjira.domain.com/rest/api/2/user"
$user = "admin"
$pass = "super secure password"
$secpasswd = ConvertTo-SecureString $user -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential($pass, $secpasswd);
$userObject = #{
name = "rkaucher#domain.net";
emailAddress = "robert_kaucher#domain.com";
displayName = "Bob Kaucher";
notification = $true;
}
$restParameters = #{
Uri = $url;
ContentType = "application/json";
Method = "POST";
Body = (ConvertTo-Json $userObject).ToString();
Credential = $cred;
}
Invoke-RestMethod #restParameters
JSON output
{
"name": "rkaucher#domain.net",
"displayName": "Bob Kaucher",
"emailAddress": "robert_kaucher#domain.com",
"notification": true
}
I changed the authentication component of my script to this:
$cred = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("$user`:$pass"))
$headers = #{Authorization=("Basic $cred")}
This was based on the selected answer to the following:
PowerShell's Invoke-RestMethod equivalent of curl -u (Basic Authentication)
The final invocation of the method looks like this:
$restParameters = #{
Uri = $url;
ContentType = "application/json";
Method = "POST";
Body = (ConvertTo-Json $userObject).ToString();
Headers = $headers;
}
$response = Invoke-RestMethod #restParameters
Related
Question
I am trying to write a PowerShell script to get report data via the MS Graph API /reports/credentialUserRegistrationDetails.
When I use Graph Explorer it works just fine, as long as I enable Reports.Read.All on the Modify permissions (Preview) tab.
But, when I try to do it with my script, I just get the error "Calling principal does not have required MSGraph permissions Reports.Read.All"
In all my searches, I can only find how to assign permissions to apps.
Is there some way to make it so I can do it from my script?
My Script
$azContext = Get-AzContext
$token = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate(
$azContext.Account,
$azContext.Environment,
$azContext.Tenant.Id,
$null,
[Microsoft.Azure.Commands.Common.Authentication.ShowDialog]::Never,
$null,
"https://graph.microsoft.com"
)
$params = #{
Method = "GET"
Uri = "https://graph.microsoft.com/beta/reports/credentialUserRegistrationDetails"
Headers = #{
Authorization = "Bearer $($token.AccessToken)"
"Content-Type" = "application/json"
}
}
Invoke-RestMethod #params
Response
{
"error": {
"code":"Authentication_MSGraphPermissionMissing",
"message":"Calling principal does not have required MSGraph permissions Reports.Read.All",
"innerError": {
"date":"2021-10-19T01:18:36",
"request-id":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"client-request-id":"6b8cc3a3-b93b-44bb-b1d4-190c618aa52a"
}
}
}
When I use Graph Explorer it works just fine, as long as I enable
Reports.Read.All on the Modify permissions (Preview) tab.
Its because Microsoft Graph Explorer is a Enterprise Application of Microsoft which is present on every Azure AD tenant just you need to sign in and use it by providing the required permissions.
But when you are writing running your Powershell script it uses Microsoft Azure Powershell . You can verify it by checking the access_token received in JWT Token.So, you need to provide the Reports.Read.All API Permission to the same app in your tenant with appid : 1950a258-227b-4e31-a9cf-717495945fc2 in Enterprise Application >> Permissions and grant the admin consent .After providing the required permissions only it will work.
Another Way will be to create a App registration ,Create a client secret for it and then provide the Reports.Read.All API permission and use the below script:
$TenantName = "tenantname.onmicrosoft.com"
$clientID = "d344e3xxx-xxx-xxxx-xxxx-9c861d363244" # app registration clientId
$clientSecret = "fNc7Q~UNHBgv_xxxxxxxxxxxxxxxxxxxxxx-PD"
$Scope = "https://graph.microsoft.com/.default"
$Body = #{
Grant_Type = "client_credentials"
Scope = $Scope
client_Id = $clientID
Client_Secret = $clientSecret
}
$authUri = "https://login.microsoftonline.com/$TenantName/oauth2/v2.0/token"
$TokenResponse = Invoke-RestMethod -Uri $authUri -Method POST -Body $Body
$Headers = #{
"Authorization" = "Bearer $($TokenResponse.access_token)"
"Content-type" = "application/json"
}
$apiUri = "https://graph.microsoft.com/beta/reports/credentialUserRegistrationDetails"
$response = Invoke-RestMethod -Headers $Headers -Uri $apiUri -Method GET
$response.value
Output:
Note: In Some Tenants Microsoft Azure PowerShell might not be visible from portal , so in that case please use the above solution it will be easier.
For Authorization code flow, try something like this -
#region Auth1
#With User Interaction for Delegated Permission
Add-Type -AssemblyName System.Web
Function Get-AuthCode {
Add-Type -AssemblyName System.Windows.Forms
$form = New-Object -TypeName System.Windows.Forms.Form -Property #{Width = 440; Height = 640 }
$web = New-Object -TypeName System.Windows.Forms.WebBrowser -Property #{Width = 420; Height = 600; Url = ($url -f ($Scope -join "%20")) }
$DocComp = {
$Global:uri = $web.Url.AbsoluteUri
if ($Global:uri -match "error=[^&]*|code=[^&]*") { $form.Close() }
}
$web.ScriptErrorsSuppressed = $true
$web.Add_DocumentCompleted($DocComp)
$form.Controls.Add($web)
$form.Add_Shown( { $form.Activate() })
$form.ShowDialog() | Out-Null
$queryOutput = [System.Web.HttpUtility]::ParseQueryString($web.Url.Query)
$output = #{}
foreach ($key in $queryOutput.Keys) {
$output["$key"] = $queryOutput[$key]
}
#$output
}
Get-AuthCode
#Extract Access token from the returned URI
$regex = '(?<=code=)(.*)(?=&)'
$authCode = ($uri | Select-string -pattern $regex).Matches[0].Value
Write-output "Received an authCode, $authCode"
$tokenBody = #{
Grant_Type = "authorization_code"
Scope = "https://graph.microsoft.com/.default"
Client_Id = $clientId
Client_Secret = $clientSecret
redirect_uri = $redirectUri
code = $authCode
ressource = $resource
}
$tokenResponse = Invoke-RestMethod https://login.microsoftonline.com/common/oauth2/token -Method Post -ContentType "application/x-www-form-urlencoded" -Body $tokenBody -ErrorAction STOP
#endregion Auth1
For delegated permissions use something like below -
$tokenBody = #{
Grant_Type = "password"
Scope = "user.read%20openid%20profile%20offline_access"
Client_Id = $clientId
username = $User
password = $pw
resource = $resource
}
$tokenResponse = Invoke-RestMethod https://login.microsoftonline.com/common/oauth2/token -Method Post -ContentType "application/x-www-form-urlencoded" -Body $tokenBody -ErrorAction STOP
#endregion Auth2
For Application permissions (using client credential flow) use something like this
$tokenBody = #{
Grant_Type = "client_credentials"
Scope = "https://graph.microsoft.com/.default"
Client_Id = $clientId
Client_Secret = $clientSecret
}
$tokenResponse = Invoke-RestMethod -Uri "https://login.microsoftonline.com/$Tenantid/oauth2/v2.0/token" -Method POST -Body $tokenBody
#endregion Auth3
Despite what Method you have chosen, the tokenRepsonse Variable is holding our Key to Query against the Microsoft GRAPH API.
We want a list of all Teams in our Tenant, so this require propriate Application Permission. So for example- our Powershell to get a Full List of all Teams look like this -
$headers = #{
"Authorization" = "Bearer $($tokenResponse.access_token)"
"Content-type" = "application/json"
}
$URL = "https://graph.microsoft.com/beta/groups?`$filter=resourceProvisioningOptions/Any(x:x eq 'Team')"
$AllTeams = (Invoke-RestMethod -Headers $headers -Uri $URL -Method GET).value
Thanks.
I finally gave up on using the REST APIs and started using Microsoft.Graph PowerShell Modules. I find the documentation pretty sparse, but at least it works for what I need. :)
Import-Module "Microsoft.Graph.Identity.Signins"
Import-Module "Microsoft.Graph.Users"
Import-Module "Microsoft.Graph.Groups"
Connect-MgGraph -TenantId $TenantId -Scopes "Directory.Read.All", "UserAuthenticationMethod.Read.All" -ForceRefresh
Select-MgProfile -Name "beta"
$report = Get-MgReportCredentialUserRegistrationDetail
Could you help me?
I am trying to create an issue in Jira using the Powershell Invoke-WebRequest cmdlet. And I am getting 400 Bad Request error.
I was able to send a successful request using Postman, so I made sure the body syntax is correct and I have sufficient rights.
My code:
$body = #{
"fields" = #{
"project"=
#{
"key"= "ProjectKey"
}
"summary"= "Test"
"description"= "Test"
"issuetype" =#{
"id"= "10705"
}
"priority"= #{
"id"= "18"
}
"reporter"= #{"name"= "MyName"}
}
}
$Headers = #{
Authorization = "Basic QWxla0Zblablablablablablabla" #I took it from Postman
}
$restapiuri = "https://jira.domain.com/rest/api/2/issue"
Invoke-RestMethod -Uri $restapiuri -ContentType "application/json" -Body $body -Method POST -Headers $Headers
for example, I can successfully execute
Invoke-RestMethod "https://jira.domain.com/rest/api/2/issue/createmeta" -Headers $Headers
I've already spent a lot of time-solving this problem, but still can't create an issue.
Any help, please đđđ
For basic authentication with Jira SERVER, the credentials need to be supplied within the header in Base64 encoding, which you need to do before supplying it via the Powershell Invoke-WebRequest method. Here's how to do it:
$username = "The username here"
$password = "The password or token here"
# Convert the username + password into a Base64 encoded hash for basic authentication
$pair = "${username}:${password}"
$bytes = [System.Text.Encoding]::ASCII.GetBytes($pair)
$base64 = [System.Convert]::ToBase64String($bytes)
$headers = #{ Authorization = "Basic $base64" }
Next, in PowerShell, if you build the body of the request as a table, like you've shown, don't don't need to wrap the table elements with inverted commas, just leave them as is, but you do need to convert the table to JSON format before submitting it, like this:
$body = #{
fields = #{
project = #{
key = "ProjectKey"
}
issuetype = #{
id = "10705" # Or refer to the Issue type by its name eg. name = "Story"
}
summary = "Test"
}
}
# Convert the body to JSON format
$body = $body | ConvertTo-Json -Depth 50
I'm assuming your $Uri string contains an actual URL to a Jira Server, not the example 'jira.domain.com'
Start with a simple request in the body, like the one I've shown, that contains only the minimum required to create the Issue, which will check your basic code is working before making the request more complex.
I'm creating PS script to automate creating snapshots of selected indices. This is my code:
# Input variables
$elastic_host = "localhost:9200";
$repo_name = "daily_backup";
$username = "elastic";
$password = "elastic";
$indices = "kibana_sample_*,test";
# Create repo if not exists
$url_put_repo = "http://$elastic_host/_snapshot/$repo_name";
$body_put_repo = #{
"type" = "fs";
"settings" = #{
"location" = "$($repo_name)_location";
"compress" = $True;
"chunk_size" = "100MB";
}
} | ConvertTo-Json;
Write-Host (Invoke-ElasticSearch $url_put_repo $username $password $body_put_repo);
# Create snapshot
$time_stamp = Get-Date -Format "yyyyMMddHHmmss";
$url_put_snapshot = "http://$elastic_host/_snapshot/$repo_name/$($time_stamp)?wait_for_completion=true";
$body_put_snapshot = #{
"indices" = $indices;
"ignore_unavailable" = $True;
"include_global_state" = $False;
} | ConvertTo-Json;
Write-Host (Invoke-ElasticSearch $url_put_snapshot $username $password, $body_put_snapshot);
function Invoke-ElasticSearch([string]$url, [string]$user, [string]$pass, [string]$body) {
$credPair = "$($user):$($pass)";
$encodedCredentials = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($credPair));
$headers = #{
"Authorization" = "Basic $encodedCredentials";
"Content-Type" = "application/json";
};
$responseData = Invoke-WebRequest -Uri $url -Method Put -Headers $headers -Body $body -UseBasicParsing -Verbose;
return $responseData;
}
Problem is that first part is working "Create repo if not exists", second part "Create snapshot" is failing:
Invoke-WebRequest : {"error":{"root_cause":[{"type":"security_exception","reason":"failed to authenticate user [elastic]","header":{"WWW-Authenticate":"Basic realm=\"security\" charset=
\"UTF-8\""}}],"type":"security_exception","reason":"failed to authenticate user [elastic]","header":{"WWW-Authenticate":"Basic realm=\"security\" charset=\"UTF-8\""}},"status":401}
When I call Invoke-ElasticSearch with no body it is not failing:
Invoke-ElasticSearch $url_put_snapshot $username $password, $body_put_snapshot
I tried this JSON in Kibana Dev Tools and it is working - I don't know where is problem. Any idea?
It was typo ofcourse...
Before:
Invoke-ElasticSearch $url_put_snapshot $username $password, $body_put_snapshot
After:
Invoke-ElasticSearch $url_put_snapshot $username $password $body_put_snapshot
$Auth = "admin:password"
$JenkinsURL = "http://$Auth#172.24.235.27:8080/"
$JobName = "TestItem1"
$JobToken = "token"
$FullURL = "$JenkinsURL/job/$JobName/build?token=$JobToken"
Invoke-WebRequest -UseBasicParsing $FullURL
Above is the PowerShell code used for triggering Jenkins job. But while executing this I am facing "Authentication required" error. But the same command from curl is working fine.
I am not sure whether I am missing something in URL or missing some Jenkins plugin to provide access from PowerShell.
The reason you are getting an authentication error is that you will need to convert the authentication to base 64 string. Below is the script that you can use if you have not enabled the CSRF in Jenkins.
$UserName = "admin"
$Password = "password"
$API_URL = "jenkinsservername"
$JobName = "TestItem1"
$JobToken = "token"
$header = #{}
$Params = #{}
$header.Add('Authorization', 'Basic ' + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("$(${UserName}):$(${Password})")))
$Params['uri'] = "http://jenkinsservername/$JobName/build?token=$JobToken"
$Params['Method'] = 'Post'
$Params['Headers'] = $header
Invoke-RestMethod #Params
But If you have CSRF Enabled in Jenkins then use below script
$UserName = "admin"
$Password = "password"
$API_URL = "jenkinsservername"
$JobName = "TestItem1"
$JobToken = "token"
$header = #{}
$header.Add('Authorization', 'Basic ' + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("$(${UserName}):$(${Password})")))
$Params = #{uri = "http://${API_URL}:${API_Port}/crumbIssuer/api/json";
Method = 'Get';
Headers = $header;}
$API_Crumb = Invoke-RestMethod #Params
write-host $API_Crumb
$h.Add('Jenkins-Crumb', $API_Crumb.crumb)
$Params['uri'] = "http://jenkinsservername/$JobName/build?token=$JobToken"
$Params['Method'] = 'Post'
$Params['Headers'] = $header
Invoke-RestMethod #Params
#Mike correctly described the instructions for working with the jenkins api when using CSRF. But I would like to add that when creating crumbs, the session and cookies are also taken into account.
The code that works for me is as follows:
$UserName = "admin"
$Password = "password"
$API_URL = "jenkinsservername"
$JobName = "TestItem1"
$JobToken = "token"
$header = #{}
$header.Add('Authorization', 'Basic ' + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("$(${UserName}):$(${Password})")))
$Params1 = #{uri = "http://${API_URL}:${API_Port}/crumbIssuer/api/json";
Method = 'Get';
SessionVariable = 'Session';
Headers = $header;}
$API_Crumb = Invoke-RestMethod #Params1
write-host $API_Crumb
$header.Add('Jenkins-Crumb', $API_Crumb.crumb)
$Params2 = #{}
$Params2['uri'] = "http://jenkinsservername/$JobName/build?token=$JobToken"
$Params2['Method'] = 'Post'
$Params2['Headers'] = $header
Invoke-RestMethod #Params2 -WebSession $Session
Also consider if the job is in a folder, then the uri will be different.
For example, if MyJob is in MyFolder then the uri will be:
http://jenkinsservername/job/MyFolder/job/MyJob/build?token=JobToken
You can see this path in the place where you assigned the token for the job
I am having a problem updating the release label to show if it is broken or Approved etc. I have got this working through postman so I know that the problem is not through that. I am using basic authentication with my username and password being entered and trying to get access to the uri using them details but for the powershell it is not working. It works fine with Postman so it's not the credentials. Here is what I am doing:
$user = Read-Host -Prompt "Enter Bamboo Username"
$pass =  Read-Host -Prompt "Enter password" -AsSecureString
$secpass = ConvertTo-SecureString $user -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential($pass,$secpass)
$baseuri = "http://bamboo.uk.myNetwork.net/rest/api/latest/deploy/version/VersionNumber/status/Approved"
$Headers = #{
'X-Atlassian-Token' = 'nochecK'
};
$json = "application/json"
Invoke-RestMethod -Uri $baseuri -Method Post -Credential $credential -Headers $Headers -ContentType $json
This is not working and I am getting the 401client must be authenticated to access this resource. I have also tried all of the following ways to authenticate the user but none have worked:
<#
$encoded = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($user+":"+$pass))
$EncodedUsernamePassword = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($('{0}:{1}' -f $Credential.UserName, $Credential.GetNetworkCredential().Password)))
$pair = "$($user):$($pass)"
$encodedCreds = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($pair))
$basicAuthValue = "Basic $encodedCreds"
#>
Postman with return the following details:
{
"id": theID,
"userName": "myUserName",
"displayName": "myName",
"creationDate": 1481536833693,
"versionState": "APPROVED"
}
This shows that the uri with basic authentication and adding the X-Atlassian-Token: nocheck works but for the powershell I am getting Authentication Errors why is this?
The problem was when I used the password as a SecureString type, when I changed it just a string it would work, so all of these ways including the one Hackerman linked worked when using the credentials without a SecureString type.