Current user as default for PSCredential parameter of custom CmdletBinding - powershell

Here's a repro of my Powershell scenario:
function Call-Api {
[CmdletBinding()]
param(
[Parameter(Position = 0, Mandatory = $false)]
[System.Management.Automation.Credential()]
[System.Management.Automation.PSCredential]
$Credential = [System.Management.Automation.PSCredential]::Empty
)
Invoke-RestMethod -Uri "http://localhost/api" -Method Get -Credential $Credential
}
The API has Basic Auth. If I call it like this:
$pwd = ConvertTo-SecureString "secret!" -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential ("username", $pwd)
Call-Api $cred
Then I get a proper response. But if I call it without credentials like this:
Call-Api
I get:
Invoke-RestMethod : Object reference not set to an instance of an object.
I could of course do an if...else thing switching on whether $Credential is set or not, but that's not very elegant. From the relevant docs I understand that the default value of that cmdlet is "the current user".
How can I do something similar? What default do I set for $Credential so that it is the current user?

You can remove the Empty Credential that you are defining as default (I believe this is overwriting the cmdlets own default of current user), then use splatting and only add the $Credential Parameter if it present. If it's not present it will use the cmdlets own default value.
function Call-Api {
[CmdletBinding()]
param(
[Parameter(Position = 0)]
[System.Management.Automation.Credential()]
[System.Management.Automation.PSCredential]
$Credential
)
$params = #{}
$params['Uri'] = "http://google.com"
$params['Method'] = "Get"
if ($Credential) { $params['Credential'] = $Credential }
Invoke-RestMethod #params
}

Related

Checking remote PowerShell session information from within a function

I've written a PS function to set a mailbox to a shared mailbox. The function works by calling a remote PS session to the exchange admin PS using
$session = New-PSSession -configurationName Microsoft.Exchange -connectionURI xxxx -Auth xxx
Import-PSSession $session
this all works fine.
But when i call the function from my leavers script it always tries to load the $session even if its already been done, this throws verious issues, but fundamentally the script works fine.
So i added a check to see if the session was already in place by using
If(($session.state -eq "closed") -or (!($session))){then load session}
but it seems to think the session doesn't exist and tries to re-import the session causing the errors. If i put a write-host $session before the IF statement it returns $null, so i don't understand why i get the error about the session already been in memory.
if i run the script outside of the function i get the expected behaviour of not loading the session if its already been loaded.
i could add a close session at the end of the function, but its not as efficient having to re-load it every time it runs.
this is the function
Function SetMailbox-ToShared {
#This function sets the mailbox to type shared
Param(
[Parameter(Mandatory=$true)]
$upn
)
#Get password details for on premises service account
# Application (client) ID, tenant Name and secret
$clientId = "xxx" #application ID
$tenantName = "xxx"
$clientSecret = "xxx"
$ReqTokenBody = #{
Grant_Type = "client_credentials"
Scope = "https://vault.azure.net/.default"
client_Id = $clientID
Client_Secret = $clientSecret
}
$TokenResponse = Invoke-RestMethod -Uri "https://login.microsoftonline.com/$TenantName/oauth2/v2.0/token" -Method POST -Body $ReqTokenBody
$secretURL = "https://xxx/secrets/xxx?api-version=2016-10-01"
$secretValue = (Invoke-RestMethod -Headers #{Authorization = "Bearer $($Tokenresponse.access_token)"} -Uri $secretURL).value
$exchUserName = "xxx"
$exchpwdTxt = "$secretValue"
$exchangeConnectionURI = "http://xxx/PowerShell/"
If(($session.state -eq "Closed") -or (!($session))){
$securePwd = $exchpwdTxt | ConvertTo-SecureString -asplaintext -force
$credObject = New-Object System.Management.Automation.PSCredential -ArgumentList $exchUserName, $securePwd
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $exchangeConnectionURI -Authentication Kerberos -Credential $credObject
Import-PSSession $Session
}
Try{
Set-RemoteMailbox $upn -type Shared
}
Catch{
Write-Host -ForegroundColor Red "Error setting mailbox for $upn to type shared. The error was $_"
}
}
Which i then call from my main script using setmailbox-toshared -upn xxx.xxx#xxx.com
i hope i've explained that ok?

-UseDefaultCredential vs. -Credential in PowerShell (401 Error)

I am writing a script in PowerShell that is testing login credentials to a target SharePoint 2013 URL that are passed through to an Invoke-WebRequest command that calls the URL in question. When I use -UseDefaultCredential, the returned status is a 200, but when I replace that and try the following code passed in to a -Credential property of the same command, I get a 401 error:
[string][ValidateNotNullOrEmpty()] $userPassword = "password"
[SecureString] $userPassword = ConvertTo-SecureString -String $userPassword -AsPlainText -Force
[PSCredential] $userCredential = New-Object -TypeName
System.Management.Automation.PSCredential("userName", $userPassword)
Invoke-Webrequest -Credential $userCredential -uri "https://connect.adler.edu"
powershell -noexit
Any thoughts as to why I'm getting the 401 Error when using the -Credential property? Thank you!

Problem to connect to TFS with user/password

When I try to connect to tfs the function Get-Data failed with 401 error although the function Get-DataWithCred succeed with the same argument.
And don't understand the difference with this two ?
function Get-Data([string]$username, [string]$password, [string]$url)
{
# Step 1. Create a username:password pair
$credPair = "$($username):$($password)"
# Step 2. Encode the pair to Base64 string
$encodedCredentials = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($credPair))
# Step 3. Form the header and add the Authorization attribute to it
$headers = #{ Authorization = "Basic $encodedCredentials" }
# Step 4. Make the GET request
$responseData = Invoke-WebRequest -Uri $url -Method Get -Headers $headers
return $responseData
}
function Get-DataWithCred([string]$username, [string]$password, [string]$url)
{
$p = ConvertTo-SecureString -String $password -AsPlainText -Force
$Cred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $username, $p
$responseData = Invoke-WebRequest -Uri $url -Method Get -Credential $Cred
return $responseData
}
The purpose is too connect through tfs with python script who failed the same way that the function Get-Data when I use the requests library.
>>> r = requests.get('https://tfs-url.com', auth=('user', 'pass'))
>>> r.status_code
401
Looks like there is a problem with $encodedCredentials.
Take a look at Choosing the right authentication mechanism
For my script who connect to TFS i use the following code :
$strUser = 'domain\userID'
$password = "YOURPASSWORD"
$strPass = ConvertTo-SecureString -String $password -AsPlainText -Force
$cred= New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList ($strUser, $strPass)
And than connect to the TFS as you did :
$responseData = Invoke-WebRequest -Uri $url -Method Get -Credential $cred
Or, If you would like to connect to the TFS with the user who runs the script you can use
-UseDefaultCredentials
code snippet :
$responseData = Invoke-WebRequest -Uri $url -Method Get -UseDefaultCredentials
You need to use a microsoft way to pass your credential : the ntlm protocol.
This protocol are not supported by default by requests but a library requests_ntlm extend requests by adding support to ntlm.
A simple example:
import os
import requests
from requests_ntlm import HttpNtlmAuth
def main():
user = "user"
password = "password"
session = requests.Session()
session.auth = HttpNtlmAuth(user, password)
url = "https://tfs-url.com"
response = session.get(url)
print(response)
if __name__ == "__main__":
main()

JIRA REST API gives HTTP error 400 with PowerShell v3

I'm attempting to write some PowerShell functions to interface with our Atlassian JIRA system (JIRA 5.2, download version). Unfortunately, I've found through trial and error that the Invoke-RestMethod doesn't seem to work (doesn't support authentication headers), so I've written up a simple function called Invoke-JiraMethod. I can confirm that this method works for GET requests; I've been able to use it to get Jira objects to my heart's desire. As soon as I tried to create an issue, though, I started getting a HTTP 400 / Bad request error.
I've followed the steps here to get my issue metadata, and I'm filling out all the required fields in my input object. Could anyone help me figure out how to solve the 400 error? I can provide more information if needed - I just didn't want to overflow the description of the question. :)
Function Invoke-JiraMethod
{
<#
.Synopsis
Low-level function that directly invokes a REST method on JIRA
.Description
Low-level function that directly invokes a REST method on JIRA. This is designed for
internal use.
#>
[CmdletBinding()]
param
(
[ValidateSet("Get","Post")] [String] $Method,
[String] $URI,
[String] $Username,
[String] $Password
)
process
{
$token = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes("${Username}:${Password}"))
$webRequest = [System.Net.WebRequest]::Create($URI)
$webRequest.Method = $Method.ToUpper()
$webRequest.AuthenticationLevel = "None"
$webRequest.Headers.Add('Authorization', "Basic $token")
#$webRequest.Headers.Add('Authorization', $token)
$webRequest.PreAuthenticate = $true
$webRequest.ContentType = "application/json"
Write-Verbose "Invoking JIRA method $Method with URI $URI"
$response = $webRequest.GetResponse()
$requestStream = $response.GetResponseStream()
$readStream = New-Object -TypeName System.IO.StreamReader -ArgumentList $requestStream
$json = $readStream.ReadToEnd()
$readStream.Close()
$response.Close()
$result = $json | ConvertFrom-Json
Write-Output $result
}
}
Function New-JiraIssue
{
param
(
[Parameter(Mandatory = $true,
Position = 0)]
[string] $ProjectKey,
[Parameter(Mandatory = $true,
Position = 1)]
[string] $IssueType,
[Parameter(Mandatory = $false)]
[string] $Priority = 3,
[Parameter(Mandatory = $true,
Position = 2)]
[string] $Summary,
[Parameter(Mandatory = $true,
Position = 3)]
[string] $Description,
[Parameter(Mandatory = $true,
Position = 4)]
[string] $Location,
[Parameter(Mandatory = $true,
Position = 5)]
[string] $Phone,
[Parameter(Mandatory = $false)]
[string] $Reporter,
[Parameter(Mandatory = $false)]
[PSCredential] $Credential
)
process
{
$ProjectObject = New-Object -TypeName PSObject -Property #{"key"=$ProjectKey}
$IssueTypeObject = New-Object -TypeName PSObject -Property #{"id"=$IssueType}
if ( -not ($Reporter))
{
Write-Verbose "Reporter not specified; defaulting to $JiraDefaultUser"
$Reporter = $JiraDefaultUser
}
$ReporterObject = New-Object -TypeName PSObject -Property #{"name"=$Reporter}
$fields = New-Object -TypeName PSObject -Property ([ordered]#{
"project"=$ProjectObject;
"summary"=$Summary;
"description"=$Description;
"issuetype"=$IssueTypeObject;
"priority"=$Priority;
"reporter"=$ReporterObject;
"labels"="";
$CustomFields["Location"]=$Location;
$CustomFields["Phone"]=$Phone;
})
$json = New-Object -TypeName PSObject -Property (#{"fields"=$fields}) | ConvertTo-Json
Write-Verbose "Created JSON object:`n$json"
# https://muwebapps.millikin.edu/jira/rest/api/latest/issue/IT-2806
# $result = Invoke-RestMethod -Uri $JiraURLIssue -Method Post -ContentType "application/json" -Body $json -Credential $Credential
if ($Username -or $Password)
{
$result = (Invoke-JiraMethod -Method Post -URI "${JiraURLIssue}" -Username $Username -Password $Password)
} else {
$result = (Invoke-JiraMethod -Method Post -URI "${JiraURLIssue}" -Username $JiraDefaultUser -Password $JiraDefaultPassword)
}
Write-Output $result
}
}
Thanks in advance!
I was receiving the error 400 because the issue type id number I had put into the json data wasn't mapped to anything. fiddler helped me diagnose that.
I used this bit of code to figure out authenticating to jira via invoke-restmethod's -Headers option: http://poshcode.org/3461
then I put the json data into a here string, and ran invoke-restmethod.
code bits below. replace "any valid... type id number" with actual project and issue type ids you get from your jira admin.
$uri = "$BaseURI/rest/api/2/issue"
$jsonString = #'
{
"fields": {
"project":
{
"id": "any valid project id number"
},
"summary": "No REST for the Wicked.",
"description": "Creating of an issue using ids for projects and issue types using the REST API",
"issuetype": {
"id": "any valid issue type id number"
}
}
}
'#
$headers = Get-HttpBasicHeader $Credentials
Invoke-RestMethod -uri $uri -Headers $headers -Method Post -Body $jsonString -ContentType "application/json"

Log on to site using powershell

I am trying to log on to an https site using a powershell script.
I've tried using a PSCredential, but I get a 401 unauthorized error when I do.
I am providing the username and password in the script. I want it to log me in without being prompted.
What is the best way to do this? Is it best to use an httprequest?
Here's what I have so far.
$userName = "username"
$secure_password = ConvertTo-SecureString "my password" -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential($userName, $secure_password)
$proxy = New-WebServiceProxy -Uri "url_for_the_download" -Credential $credential
Something like this should work:
$client = New-Object System.Net.Webclient
$client.Credentials = New-Object System.Net.NetworkCredential("user","pass")
$client.DownloadFile("http://somesite.com/somefile.zip","C:\somefile.zip")