This in continuation to one of my previous queries Invoke-WebRequest equivalent in PowerShell v2
Im trying to run the below script, it ran 2-3 timed but after that im repeatedly getting the below timed out error
Exception calling "GetRequestStream" with "0" argument(s): "The operation has timed out"
Here's the script
function Invoke_Workflow {
param(
[Parameter(Mandatory=$True)]
[string]$arg,
[Parameter(Mandatory=$True)]
[string]$whostname,
[Parameter(Mandatory=$True)]
[string]$uuid,
[Parameter(Mandatory=$True)]
[string]$wfname,
[Parameter(Mandatory=$True)]
[string]$wpassword,
[Parameter(Mandatory=$True)]
[string]$wusername
)
$body =
"<wInput>
<userInputValues>
<userInputEntry value='$arg' key='stringArgument'/>
</userInputValues>
<executionDateAndTime></executionDateAndTime>
<comments></comments>
</wInput>"
# Disable certificate validation using certificate policy that ignores all certificates
add-type #"
using System.Net;
using System.Security.Cryptography.X509Certificates;
public class IDontCarePolicy : ICertificatePolicy {
public IDontCarePolicy() {}
public bool CheckValidationResult(
ServicePoint sPoint, X509Certificate cert,
WebRequest wRequest, int certProb) {
return true;
}
}
"#
[System.Net.ServicePointManager]::CertificatePolicy = new-object IDontCarePolicy
$password = ConvertTo-SecureString $wfapassword -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential ($wfausername, $password)
$request = [System.Net.WebRequest]::Create($URI1)
$request.ContentType = "application/xml"
$request.Method = "POST"
$request.Credentials = $credential
# $request | Get-Member for a list of methods and properties
try
{
$request.ServicePoint.MaxIdleTime = 5000;
$request.ServicePoint.ConnectionLeaseTimeout = 5000;
$requestStream = $request.GetRequestStream()
$streamWriter = New-Object System.IO.StreamWriter($requestStream)
$streamWriter.Write($body)
}
finally
{
if ($null -ne $streamWriter) { $streamWriter.Dispose() }
if ($null -ne $requestStream) { $requestStream.Dispose() }
}
$res = $request.GetResponse()
$request.Abort()
}
Problem is most likely related to the default Servicepoint connection limit which is 2.
You can increase it (10 as an example below) and if you are making many requests in a single powershell session then adding a check of the currentconnection count and clearing them (8 as an example below) prior to reaching the set connection limit:
$request.ServicePoint.ConnectionLimit =10;
$conn=$request.ServicePoint.CurrentConnections
If ($conn -ge 8) {
$request.ServicePoint.CloseConnectionGroup("")
}
Related
I am loocking for an option to disable the SSL cert validation for a single websocket connection. My server in question uses a self-signed certificate only.
This is the Powershell code that I have so far:
$server = 'server1'
$cred = Get-Credential -Message "Credentials to access '$server'"
# setting credentials for any required proxy-authentication:
[System.Net.WebRequest]::DefaultWebProxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials
# define settings for http-client:
Add-Type -AssemblyName System.Net.Http
$handler = [System.Net.Http.HttpClientHandler]::new()
$ignoreCerts = [System.Net.Http.HttpClientHandler]::DangerousAcceptAnyServerCertificateValidator
$handler.ServerCertificateCustomValidationCallback = $ignoreCerts
$handler.Credentials = $cred
$handler.PreAuthenticate = $true
$client = [System.Net.Http.HttpClient]::new($handler)
# generate websocket-key:
$init = [guid]::NewGuid().Guid.Substring(0,16)
$bytes = [System.Text.Encoding]::UTF8.GetBytes($init)
$key = [Convert]::ToBase64String($bytes)
# request protocol switch:
$url = [string]::Concat('https://', $server)
$client.DefaultRequestHeaders.Add('Connection', 'Upgrade,Keep-Alive')
$client.DefaultRequestHeaders.Add('Upgrade', 'WebSocket')
$client.DefaultRequestHeaders.Add('Sec-WebSocket-Version', '13')
$client.DefaultRequestHeaders.Add('Sec-WebSocket-Key', $key)
$client.DefaultRequestHeaders.Add('Origin', "wss://$server")
$response = $client.GetAsync($url).result
if ($response.StatusCode -eq 'BadGateway'){break}
Here I need a better solution than this "big bang" approach. In best case I am able to disable the cert validation only for this single websocket client/connection like I could do it for the above http-handler:
# skip SSL-Validation for websocket communication:
add-type #"
using System.Net;
using System.Security.Cryptography.X509Certificates;
public class TrustAllCertsPolicy : ICertificatePolicy {
public bool CheckValidationResult(
ServicePoint srvPoint, X509Certificate certificate,
WebRequest request, int certificateProblem) {
return true;
}
}
"#
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
If cert-validation is disabled, then we can do a quick connection-test to the server:
$ws = [System.Net.WebSockets.ClientWebSocket]::new()
$ct = [System.Threading.CancellationToken]::new($false)
$url = [string]::Concat('wss://', $server)
# connect to server:
$conn = $ws.ConnectAsync($url, $ct)
$null = [Threading.Tasks.Task]::WaitAll($conn)
# check websocket-status:
$ws | ft -AutoSize
Can anyone advise on how to validate credentials on a remote domain?
My environment has multiple domains that do not have trust relationships defined between them.
I have a Powershell script that needs to access a shared folder residing on a server in another domain which obviously requires authentication. Prior to accessing it, I need to validate credentials to avoid lock-outs (The script can be ran against multiple servers).
In the past I've used this wonderful script which used current domain for validation but I cannot get it to work against a remote domain.
I tried this is (slightly modified script from link above):
function Test-Cred {
[CmdletBinding()]
[OutputType([String])]
Param (
[Parameter(
Mandatory = $false,
ValueFromPipeLine = $true,
ValueFromPipelineByPropertyName = $true
)]
[Alias(
'PSCredential'
)]
[ValidateNotNull()]
[System.Management.Automation.PSCredential]
[System.Management.Automation.Credential()]
$Credentials
)
$Domain = $null
$Root = $null
$Username = $null
$Password = $null
If($Credentials -eq $null)
{
Try
{
$Credentials = Get-Credential "domain\$env:username" -ErrorAction Stop
}
Catch
{
$ErrorMsg = $_.Exception.Message
Write-Warning "Failed to validate credentials: $ErrorMsg "
Pause
Break
}
}
# Checking module
Try
{
# Split username and password
$Username = $credentials.username
$Password = $credentials.GetNetworkCredential().password
# Get Domain
###$Root = "LDAP://" + ([ADSI]'').distinguishedName
$Root = "LDAP://DC=remote_domain,DC=com" ### statically define the remote domain
$Domain = New-Object System.DirectoryServices.DirectoryEntry($Root,$UserName,$Password)
}
Catch
{
$_.Exception.Message
Continue
}
If(!$domain)
{
Write-Warning "Something went wrong"
}
Else
{
If ($domain.name -ne $null)
{
return "Authenticated"
}
Else
{
$Domain ### diagnosing the error
return "Not authenticated"
}
}
}
I get the following error:
format-default : The following exception occurred while retrieving member "distinguishedName": "The user name or
password is incorrect.
"
+ CategoryInfo : NotSpecified: (:) [format-default], ExtendedTypeSystemException
+ FullyQualifiedErrorId : CatchFromBaseGetMember,Microsoft.PowerShell.Commands.FormatDefaultCommand
The username/password is 100% correct.
Thank you
EDIT 1
I have found the following blog post that goes over how to work with Active Directory using .Net assemblies. The following has worked quite well
Add-Type -AssemblyName System.DirectoryServices.AccountManagement
#store credentials (of account with appropriate permissions)
$creds = Get-Credential
#set the domain name
$dn = 'contoso.com'
#Create the principal context object (so to say connect to a domain with the credentials provided)
$pc = [System.DirectoryServices.AccountManagement.PrincipalContext]::new([System.DirectoryServices.AccountManagement.ContextType]::`
Domain,$dn,$($creds.UserName),$($creds.GetNetworkCredential().Password))
I assume I could use this in an If statement to achieve what I need. Admittedly, I do not know the way of the .Net and it is a bit scary but I will have to learn it.
EDIT 2
Here is what I pieced together:
Function Test-Cred
{
[CmdletBinding()]
[OutputType([String])]
Param (
[Parameter(
Mandatory = $false,
ValueFromPipeLine = $true,
ValueFromPipelineByPropertyName = $true
)]
[Alias(
'PSCredential'
)]
[ValidateNotNull()]
[System.Management.Automation.PSCredential]
[System.Management.Automation.Credential()]
$Credentials
)
Add-Type -AssemblyName System.DirectoryServices.AccountManagement
# Checking module
$Validated = [System.DirectoryServices.AccountManagement.PrincipalContext]::new([System.DirectoryServices.AccountManagement.ContextType]::Domain,'remote_domain',$($Credentials.UserName),$($Credentials.GetNetworkCredential().Password))
If ($Validated.ConnectedServer)
{
Return "Authenticated"
}
Else
{
Return "Not authenticated"
}
}
Any feedback?
EDIT 3
Well, EDIT 2 does not work for Powershell 4, grrr
Method invocation failed because [System.DirectoryServices.AccountManagement.PrincipalContext] dies not contain method named 'new'
I had to make it work like this:
$ContextType = [System.DirectoryServices.AccountManagement.ContextType]::Domain
$ContextName = 'target_domain.com'
$Validated = New-Object -TypeName System.DirectoryServices.AccountManagement.PrincipalContext -ArgumentList $ContextType, $ContextName, $($Credentials.UserName),$($Credentials.GetNetworkCredential().Password)
Here is my final version of this test function that works with Powershell version older than 5.1.
Function Test-Cred
{
[CmdletBinding()]
[OutputType([String])]
Param (
[Parameter(
Mandatory = $false,
ValueFromPipeLine = $true,
ValueFromPipelineByPropertyName = $true
)]
[Alias(
'PSCredential'
)]
[ValidateNotNull()]
[System.Management.Automation.PSCredential]
[System.Management.Automation.Credential()]
$Credentials
)
# Checking module
Add-Type -AssemblyName System.DirectoryServices.AccountManagement
$ContextType = [System.DirectoryServices.AccountManagement.ContextType]::Domain
$ContextName = 'remote_domain.com'
$Validated = New-Object -TypeName System.DirectoryServices.AccountManagement.PrincipalContext -ArgumentList $ContextType, $ContextName, $($Credentials.UserName),$($Credentials.GetNetworkCredential().Password)
If ($Validated.ConnectedServer)
{
Return "Authenticated"
}
Else
{
Return "Not authenticated"
}
}
I have a question about powershell, I have a script which allows me to connect to a secure url which asks me username and password, I manage to connect, and I receive as output an html file with entries, my question is how to fill in these inputs and submit ?
Explore the $o_response variable to check the return values. Call it by: fcn-check-site -UNAME username -UPASS password -URI secureURL
function fcn-check-site {
Param (
[ Parameter( Mandatory = $true ) ]
$UNAME,
[ Parameter( Mandatory = $true ) ]
$UPASS,
[ Parameter( Mandatory = $true ) ]
$URI
)
Begin {
$s_username = $UNAME
$s_password = $UPASS
$s_URI = $URI
}
Process {
try {
# Create a Web Request object. This object can have different properties/payloads attached to be sent to the URL in question.
$o_webrequest = [System.Net.WebRequest]::Create( $s_URI )
$o_secpasswd = $s_password | ConvertTo-SecureString -AsPlainText -Force -ErrorAction Stop
$o_mycreds = New-Object System.Management.Automation.PSCredential ($s_username,$o_secpasswd) -ErrorAction Stop
# The connection test is to check if the login returns a 'good/ok/200' value. Hence, nothing is POST-ed. Only GET-ted.
$o_webrequest.Method = 'GET'
$o_webrequest.Credentials = $o_mycreds
# 30 second timeout on the request to be made.
$o_webrequest.Timeout = 30000
# This C# code makes sure that SSL certificate checks are skipped.
try {
# This erroractionpreference variable will STOP the compilaton errors thrown by C#.
$ErrorActionPreference = 'Stop'
add-type -ErrorAction Stop -TypeDefinition #"
using System.Net;
using System.Security.Cryptography.X509Certificates;
public class TrustAllCertsPolicy : ICertificatePolicy {
public bool CheckValidationResult(
ServicePoint srvPoint, X509Certificate certificate,
WebRequest request, int certificateProblem) {
return true;
}
}
"#
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy -ErrorAction SilentlyContinue
$o_response = $o_webrequest.GetResponse()
if( ( $o_response.StatusCode -eq 'OK' ) -or `
( $o_response.StatusCode -eq 200 ) ) {
"$( $URI ) is accessible."
# Check the contents of $o_response to see what is returned.
}
else {
"$( $URI ) is NOT accessible. Response Status is : $( $o_response.StatusCode )"
}
}
catch {
"[ERROR] : $( $Error[ 0 ].ToString() )"
}
}
catch {
if( $Error[ 0 ].ToString() -match "Unable to connect to the remote server" ) {
"$( $URI ) is NOT accessible."
}
else {
"[ERROR] : $( $Error[ 0 ].ToString() )"
}
}
}
}
I want to upload a file using powershell to a RestAPI. Sadly the invoke-restmethod does not support MultipartForm Uploads and even with building my own body it does not work. Therefore, I have to get this working using another way :)
Luckily I found this excellent blog post: http://blog.majcica.com/2016/01/13/powershell-tips-and-tricks-multipartform-data-requests/ and now I'm trying to build me a request using the .NET Client within Powershell. But I do hit my borders right now. I need to edit the standard header to add an authorization token. And I have no clue how! Would somebody please so kind and point me in the right direction how to do this? Thank you very much!
function Invoke-MultipartFormDataUpload
{
[CmdletBinding()]
PARAM
(
[string][parameter(Mandatory = $true)][ValidateNotNullOrEmpty()]$InFile,
[string]$ContentType,
[Uri][parameter(Mandatory = $true)][ValidateNotNullOrEmpty()]$Uri,
[System.Management.Automation.PSCredential]$Credential
)
BEGIN
{
if (-not (Test-Path $InFile))
{
$errorMessage = ("File {0} missing or unable to read." -f $InFile)
$exception = New-Object System.Exception $errorMessage
$errorRecord = New-Object System.Management.Automation.ErrorRecord $exception, 'MultipartFormDataUpload', ([System.Management.Automation.ErrorCategory]::InvalidArgument), $InFile
$PSCmdlet.ThrowTerminatingError($errorRecord)
}
if (-not $ContentType)
{
Add-Type -AssemblyName System.Web
$mimeType = [System.Web.MimeMapping]::GetMimeMapping($InFile)
if ($mimeType)
{
$ContentType = $mimeType
}
else
{
$ContentType = "application/octet-stream"
}
}
}
PROCESS
{
Add-Type -AssemblyName System.Net.Http
$httpClientHandler = New-Object System.Net.Http.HttpClientHandler
if ($Credential)
{
$networkCredential = New-Object System.Net.NetworkCredential #($Credential.UserName, $Credential.Password)
$httpClientHandler.Credentials = $networkCredential
}
$httpClient = New-Object System.Net.Http.Httpclient $httpClientHandler
$packageFileStream = New-Object System.IO.FileStream #($InFile, [System.IO.FileMode]::Open)
$contentDispositionHeaderValue = New-Object System.Net.Http.Headers.ContentDispositionHeaderValue "form-data"
$contentDispositionHeaderValue.Name = "fileData"
$contentDispositionHeaderValue.FileName = (Split-Path $InFile -leaf)
$streamContent = New-Object System.Net.Http.StreamContent $packageFileStream
$streamContent.Headers.ContentDisposition = $contentDispositionHeaderValue
$streamContent.Headers.ContentType = New-Object System.Net.Http.Headers.MediaTypeHeaderValue $ContentType
$content = New-Object System.Net.Http.MultipartFormDataContent
$content.Add($streamContent)
try
{
$response = $httpClient.PostAsync($Uri, $content).Result
if (!$response.IsSuccessStatusCode)
{
$responseBody = $response.Content.ReadAsStringAsync().Result
$errorMessage = "Status code {0}. Reason {1}. Server reported the following message: {2}." -f $response.StatusCode, $response.ReasonPhrase, $responseBody
throw [System.Net.Http.HttpRequestException] $errorMessage
}
return $response.Content.ReadAsStringAsync().Result
}
catch [Exception]
{
$PSCmdlet.ThrowTerminatingError($_)
}
finally
{
if($null -ne $httpClient)
{
$httpClient.Dispose()
}
if($null -ne $response)
{
$response.Dispose()
}
}
}
END { }
}
Try this
#Authorization
$httpClient.DefaultRequestHeaders.Authorization = New-Object System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", $accesstoken);
After attempting several different examples I am unable to update list items using the Sharepoint REST API. I receive back a 400 error from the request.
The creplace is due to Sharepoint sending both ID and Id for some reason and breaking the ConvertFrom-Json in my Get-SPListItems method.
function Update-SPListItems
{
param
(
$listUpdate
)
$requestDigest = Get-RequestDigest
foreach($item in $listUpdate.Results)
{
$restUrl = $item.__metadata.uri
$item.tsFeedbackStatus = "Open"
$item.Modified = Get-Date -Format s
$updatedItem = $item | ConvertTo-Json
#convert back the duplicate field
$updatedItem = $updatedItem -creplace '"ignoreId":','"Id":'
$itemJsonBytes = [System.Text.Encoding]::ASCII.GetBytes($updatedItem)
try
{
#examples have shown POST/MERGE, POST/PATCH, MERGE/MERGE,
#PATCH/PATCH, none of them in those combinations have worked
$request = [System.Net.WebRequest]::Create($restUrl)
$request.Credentials = $Credential.GetNetworkCredential()
$request.Headers.Add("X-FORMS_BASED_AUTH_ACCEPTED", "f")
$request.Headers.Add("If-Match", "*")
$request.Headers.Add("X-RequestDigest", $requestDigest)
$request.Headers.Add("X-HTTP-Method", "MERGE")
$request.Accept = "application/json;odata=verbose"
$request.Method = "POST"
$request.ContentType = "application/json;odata=verbose"
$request.ContentLength = $itemJsonBytes.Length
$stream = $request.GetRequestStream()
$stream.Write($itemJsonBytes, 0, $itemJsonBytes.Length)
$stream.Close()
$response = $request.GetResponse()
}
catch [System.Exception]
{
Write-Error $_.Exception.ToString()
}
}
}
Here is the exact error:
Update-SPListItems : System.Net.WebException: The remote server returned an error: (400) Bad Request.
at System.Net.HttpWebRequest.GetResponse()
at CallSite.Target(Closure , CallSite , Object )
At C:\Users\user\Desktop\SPListTest.ps1:120 char:11
$result = Update-SPListItems $list
~~~~~~~~~~~~~~~~~~~~~~~~
CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Update-SPListItems
Try the below reusable function for this. It worked for me every time. Ensure to pass the parameters properly. This has authentication built into it and even works for older versions of PowerShell.
function Request-Rest{
[CmdletBinding()]
PARAM (
[Parameter(Mandatory=$False)]
[String]$Metadata,
[Parameter(Mandatory=$true)]
[String] $URL,
[Parameter(Mandatory=$False)]
[Switch]$listUpdate,
[Parameter(Mandatory=$False)]
[String]$RequestDigest,
[Parameter(Mandatory=$false)]
[Microsoft.SharePoint.Client.SharePointOnlineCredentials] $credentials,
[Parameter(Mandatory=$false)]
[String] $UserAgent = "PowerShell API Client",
[Parameter(Mandatory=$false)]
[Switch] $JSON,
[Parameter(Mandatory=$false)]
[Switch] $Raw
)
#Create a URI instance since the HttpWebRequest.Create Method will escape the URL by default.
#$URL = Fix-Url $Url
$URI = New-Object System.Uri($URL,$true)
#Create a request object using the URI
$request = [System.Net.HttpWebRequest]::Create($URI)
#Build up a User Agent
$request.UserAgent = $(
"{0} (PowerShell {1}; .NET CLR {2}; {3})" -f $UserAgent, $(if($Host.Version){$Host.Version}else{"1.0"}),
[Environment]::Version,
[Environment]::OSVersion.ToString().Replace("Microsoft Windows ", "Win")
)
if ($credentials -eq $null)
{
$request.UseDefaultCredentials = $true
}
else
{
$request.Credentials = $credentials
}
$request.Headers.Add("X-FORMS_BASED_AUTH_ACCEPTED", "f")
#Request Method
$request.Method = "POST"
#Headers
if($listUpdate)
{
$request.Headers.Add("X-RequestDigest", $RequestDigest)
$request.Headers.Add("If-Match", "*")
$request.Headers.Add("X-HTTP-Method", "MERGE")
$request.ContentType = "application/json;odata=verbose"
$request.Accept = "application/json;odata=verbose"
}
#Request Body
if($Metadata) {
$Body = [byte[]][char[]]$Metadata
$request.ContentLength = $Body.Length
$stream = $request.GetRequestStream()
$stream.Write($Body, 0, $Body.Length)
}
else {
$request.ContentLength = 0
}
try
{
[System.Net.HttpWebResponse] $response = [System.Net.HttpWebResponse] $request.GetResponse()
}
catch
{
Throw "Exception occurred in $($MyInvocation.MyCommand): `n$($_.Exception.Message)"
}
$reader = [IO.StreamReader] $response.GetResponseStream()
if (($PSBoundParameters.ContainsKey('JSON')) -or ($PSBoundParameters.ContainsKey('Raw')))
{
$output = $reader.ReadToEnd()
}
else
{
$output = $reader.ReadToEnd()
}
$reader.Close()
if($output.StartsWith("<?xml"))
{
[xml]$outputXML = [xml]$output
}
else
{
[xml]$outputXML = [xml] ("<xml>" + $output + "</xml>")
}
Write-Output $outputXML
$response.Close()
}