Azure devops: Screenshot are not showing in attachment tab - selenium-webdriver

I am trying to add failed test attachments in the Test tab in Azure DevOps using VS test task.
I am calling the Create Test Result Attachment rest api,
$AzureDevOpsPAT = {PAT}
$AzureDevOpsAuthenicationHeader = #{Authorization = 'Basic ' + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$($AzureDevOpsPAT)")) }
$result = Invoke-RestMethod -Uri https://dev.azure.com/{org}/{proj}/_apis/test/runs/$(runId)/results?api-version=6.0 -Headers $AzureDevOpsAuthenicationHeader -Method Get
#List all test result get the test result ID via result
foreach($Run in $result.value){
#Get the test result ID via result
If($Run.outcome -eq "Failed"){
$TestResultID = $Run.id
$Name=$Run.testCase.name
Write-Host $TestResultID $Name
#Add attachment via test run ID and test result ID
$TestResultAttachmentURL = "https://dev.azure.com/{org}/{proj}/_apis/test/Runs/$(runId)/results/$($TestResultID)/attachments?api-version=6.0-preview.1"
$body =#"
{
"stream": "abc",
"fileName": "$(System.DefaultWorkingDirectory)/$Name.png",
"comment": "Test attachment upload",
"attachmentType": "GeneralAttachment"
}
"#
$TestResultAttachmentResult = Invoke-RestMethod -Uri $TestResultAttachmentURL -ContentType "application/json" -Body $body -Headers $AzureDevOpsAuthenicationHeader -Method POST
}
}
I cant see the respective screenshot, the black flower is showing if I click on .png file in the attachment tab,
Though I am capturing a screenshot in my code too,
protected void StopWebDriver()
{
if (WebDriver != null)
{
string path = Directory.GetCurrentDirectory() + "\\" + BaseConfig.TestCaseName + ".png";
((ITakesScreenshot)WebDriver).GetScreenshot().SaveAsFile(path, ScreenshotImageFormat.Png);
WebDriver.Close();
WebDriver.Quit();
}
}
Can anyone please tell me how can I see screenshots?

Based on my test , the screenshot could show in the Pipeline -> Test -> Attachment tab.
You could use the PowerShell Script to generate the Base64 file Stream instead of hardcode the file stream.
Here is an example:
$file= [IO.File]::ReadAllBytes("filepath\$Name.png")
$Base64file= [Convert]::ToBase64String($file)
echo $Base64file
$token = "PAT"
$url="https://dev.azure.com/{org}/{proj}/_apis/test/Runs/$(runId)/results/$($TestResultID)/attachments?api-version=6.0-preview.1"
$token = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($token)"))
$JSON = "
{
`"stream`": `"$Base64file`",
`"fileName`": `"$Name.png`",
`"comment`": `"Test attachment upload`",
`"attachmentType`": `"GeneralAttachment`"
}"
$response = Invoke-RestMethod -Uri $url -Headers #{Authorization = "Basic $token"} -Method Post -Body $JSON -ContentType application/json
On the other hand, you could directly try to use the Publish screenshots for test failure task from the extension Publish test result screenshot.
In addition, in order to confirm whether the image you uploaded is valid, you can also download and check whether it is the correct content.
Update:
To get the Test case name in a string, you could refer to this sample:
$String= "ooo.iii.kkk.lll"
$a,$b,$c,$d = $String.Split(".",4)
echo $c

Powershell script task1 to get the run id:
$AzureDevOpsPAT = {PAT}
$AzureDevOpsAuthenicationHeader = #{Authorization = 'Basic ' + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$($AzureDevOpsPAT)")) }
$UriOrga = "https://dev.azure.com/{org}/{proj}/"
$uriAccount = $UriOrga + "_apis/test/runs?api-version=6.0"
$response = Invoke-RestMethod -Uri $uriAccount -Headers $AzureDevOpsAuthenicationHeader -Method Get
$testRunsIdSorted = $response.value | sort-object id -Descending
$result = Invoke-RestMethod -Uri https://dev.azure.com/{org}/{proj}/_apis/test/runs/$($testRunsIdSorted[0].id)?api-version=6.0 -Headers $AzureDevOpsAuthenicationHeader -Method Get
Write-Host "results = $($result | ConvertTo-Json -Depth 100)"
Write-Host "##vso[task.setvariable variable=runId]$($result.id | ConvertTo-Json -Depth 100)"
Powershell task2 to attach the screenshot attachment:
$AzureDevOpsPAT = {PAT}
$AzureDevOpsAuthenicationHeader = #{Authorization = 'Basic ' + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$($AzureDevOpsPAT)")) }
$result = Invoke-RestMethod -Uri https://dev.azure.com/{org}/{proj}/_apis/test/runs/$(runId)/results?api-version=6.0 -Headers $AzureDevOpsAuthenicationHeader -Method Get
#List all test result get the test result ID via result
foreach($Run in $result.value){
#Get the test result ID via result
If($Run.outcome -eq "Failed"){
$TestResultID = $Run.id
$TestTitle=$Run.testCase.name
$CharArray =$TestTitle.Split(".")
$TestCase=$CharArray[6]
$CharArraytwo=$TestCase.Split("(")
$TestName =$CharArraytwo[0]
$file= [IO.File]::ReadAllBytes("$(System.DefaultWorkingDirectory)\{Source alias}\tests\{testproject}\bin\Release\$TestName.png")
$Base64file= [Convert]::ToBase64String($file)
#Add attachment via test run ID and test result ID
$TestResultAttachmentURL = "https://dev.azure.com/{org}/{proj}/_apis/test/Runs/$(runId)/results/$($TestResultID)/attachments?api-version=6.0-preview.1"
$body =#"
{
"stream": "$Base64file",
"fileName": "$TestName.png",
"comment": "Test attachment upload",
"attachmentType": "GeneralAttachment"
}
"#
$TestResultAttachmentResult = Invoke-RestMethod -Uri $TestResultAttachmentURL -ContentType "application/json" -Body $body -Headers $AzureDevOpsAuthenicationHeader -Method POST
}
}
The C# code was correct. In VS test task, PowerShell tasks remember to enable "Conitnue on error". Otherwise, if the task failed, others will not execute.

Related

How do I use Bicep (or ARM) to create an AD app registration and roles?

I'm using Terraform to create app registration and roles for our apps. But I cannot figure out how to do the same with Bicep. This is what's used today:
Step 1. Register the app in Active Directory, effectively creating an "app registration".
resource "azuread_application" "ad_app" {
name = local.full_app_name
type = "webapp/api"
owners = var.app_owners
}
Step 2: Create a role for our app
resource "azuread_application_app_role" "person_read" {
application_object_id = azuread_application.ad_app.id
allowed_member_types = ["Application"]
description = "Person Reader can search and read persons"
display_name = "Person Reader"
value = "Persons.Read"
}
Problem is I cannot figure out how to do those steps with Bicep (or ARM templates). I tried with 'Microsoft.Authorization/roleDefinitions', but it doesn't seem right. And I have no idea about how to do the app registration.
Unfortunately both are not directly supported in ARM template or Bicep. But You can use Deployment Scripts to create both using Bicep/ARM template.
Create Azure AD App registration using Bicep:
param name string
param location string = resourceGroup().location
param currentTime string = utcNow()
resource script 'Microsoft.Resources/deploymentScripts#2019-10-01-preview' = {
name: name
location: location
kind: 'AzurePowerShell'
identity: {
type: 'UserAssigned'
userAssignedIdentities: {
'${resourceId('app-reg-automation', 'Microsoft.ManagedIdentity/userAssignedIdentities', 'AppRegCreator')}': {}
}
}
properties: {
azPowerShellVersion: '5.0'
arguments: '-resourceName "${name}"'
scriptContent: '''
param([string] $resourceName)
$token = (Get-AzAccessToken -ResourceUrl https://graph.microsoft.com).Token
$headers = #{'Content-Type' = 'application/json'; 'Authorization' = 'Bearer ' + $token}
$template = #{
displayName = $resourceName
requiredResourceAccess = #(
#{
resourceAppId = "00000003-0000-0000-c000-000000000000"
resourceAccess = #(
#{
id = "e1fe6dd8-ba31-4d61-89e7-88639da4683d"
type = "Scope"
}
)
}
)
signInAudience = "AzureADMyOrg"
}
// Upsert App registration
$app = (Invoke-RestMethod -Method Get -Headers $headers -Uri "https://graph.microsoft.com/beta/applications?filter=displayName eq '$($resourceName)'").value
$principal = #{}
if ($app) {
$ignore = Invoke-RestMethod -Method Patch -Headers $headers -Uri "https://graph.microsoft.com/beta/applications/$($app.id)" -Body ($template | ConvertTo-Json -Depth 10)
$principal = (Invoke-RestMethod -Method Get -Headers $headers -Uri "https://graph.microsoft.com/beta/servicePrincipals?filter=appId eq '$($app.appId)'").value
} else {
$app = (Invoke-RestMethod -Method Post -Headers $headers -Uri "https://graph.microsoft.com/beta/applications" -Body ($template | ConvertTo-Json -Depth 10))
$principal = Invoke-RestMethod -Method POST -Headers $headers -Uri "https://graph.microsoft.com/beta/servicePrincipals" -Body (#{ "appId" = $app.appId } | ConvertTo-Json)
}
// Creating client secret
$app = (Invoke-RestMethod -Method Get -Headers $headers -Uri "https://graph.microsoft.com/beta/applications/$($app.id)")
foreach ($password in $app.passwordCredentials) {
Write-Host "Deleting secret with id: $($password.keyId)"
$body = #{
"keyId" = $password.keyId
}
$ignore = Invoke-RestMethod -Method POST -Headers $headers -Uri "https://graph.microsoft.com/beta/applications/$($app.id)/removePassword" -Body ($body | ConvertTo-Json)
}
$body = #{
"passwordCredential" = #{
"displayName"= "Client Secret"
}
}
$secret = (Invoke-RestMethod -Method POST -Headers $headers -Uri "https://graph.microsoft.com/beta/applications/$($app.id)/addPassword" -Body ($body | ConvertTo-Json)).secretText
$DeploymentScriptOutputs = #{}
$DeploymentScriptOutputs['objectId'] = $app.id
$DeploymentScriptOutputs['clientId'] = $app.appId
$DeploymentScriptOutputs['clientSecret'] = $secret
$DeploymentScriptOutputs['principalId'] = $principal.id
// create app role
'''
cleanupPreference: 'OnSuccess'
retentionInterval: 'P1D'
forceUpdateTag: currentTime // ensures script will run every time
}
}
output objectId string = script.properties.outputs.objectId
output clientId string = script.properties.outputs.clientId
output clientSecret string = script.properties.outputs.clientSecret
output principalId string = script.properties.outputs.principalId
Reference:
Creating App Registration with ARM templates/Bicep | by Jon Reginbald
Creating a App Roles for Azure AD application:
I don't have much idea on this but I guess you can use the below script where //create app role is written in the above code:
$app = (Invoke-RestMethod -Method Get -Headers $headers -Uri "https://graph.microsoft.com/beta/applications/$($app.id)")
$body1 = #{
Id = [Guid]::NewGuid().ToString()
IsEnabled = true
AllowedMemberTypes =#("application")
Description = "My Role Description.."
DisplayName = "My Custom Role"
Value = "MyCustomRole"
}
$createapprole= Invoke-RestMethod -Method POST -Headers $headers -Uri "https://graph.microsoft.com/beta/applications/$($app.id)/appRoles" -Body ($body1 | ConvertTo-Json)
Reference:
appRole resource type
Update application

Image not displayed in the mail using powershell script [duplicate]

I am editing one of my old scripts to send an email to a user with images embedded into the text. I am trying to use the Send-MailMessage function to send the email as opposed to the older method of $smtp.send($msg). However, when trying to update the script, the images are no longer being embedded.
I know how to attach them to the email as actual attachments, but I am not sure what I am doing wrong to have them show as actual embedded images.
NOTE: for brevity, I removed some of the full email since it is large and as long as I can get an image or two working, it will all work.
# force powershell to run as an x86 process
Set-ExecutionPolicy -Scope CurrentUser Unrestricted
if ($env:Processor_Architecture -ne "x86") {
&"$env:windir\syswow64\windowspowershell\v1.0\powershell.exe" -file $myinvocation.Mycommand.path
exit
}
# initialize the script
if ($startupvariables) { try {Remove-Variable -Name startupvariables -Scope Global -ErrorAction SilentlyContinue } catch { } }
New-Variable -force -name startupVariables -value ( Get-Variable | ForEach-Object { $_.Name } )
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic') | Out-Null
$Separator = ".", "#"
# advise what the script does
Add-Type -AssemblyName PresentationCore,PresentationFramework
$ButtonType = [System.Windows.MessageBoxButton]::OKCancel
$MessageIcon = [System.Windows.MessageBoxImage]::Warning
$MessageTitle = "Shared Drive Access Assistance"
$MessageBody = "This script asks the user to provide more information regarding a network drive that they would like access to.`n`nTo use it, enter the below information:`n`n`n`tTicket Number`n`n`tUser's Email Address`n`n`tRequestor's Email Address`n`n`nIf this is the script you want to use, click OK.`nIf not, click Cancel."
$Result = [System.Windows.MessageBox]::Show($MessageBody,$MessageTitle,$ButtonType,$MessageIcon)
if ($Result -eq "Cancel")
{
Exit-PSSession
}
else
{
# get the ticket number
$Ticket = [Microsoft.VisualBasic.Interaction]::InputBox("Enter the SCTask ticket number" , "Ticket Number")
# get the user id via the email address
$UserID = [Microsoft.VisualBasic.Interaction]::InputBox("Enter the user's email address" , "User Email Address")
$User = $UserID.split($Separator)
$Firstname = $User[0].substring(0,1).toupper()+$User[0].substring(1).tolower()
$Lastname = $User[1].substring(0,1).toupper()+$User[1].substring(1).tolower()
$User = $Firstname, $Lastname
# get local username
$Username = [System.Environment]::UserName
# create email
$subject = "Ticket $Ticket on Hold for User Response - Shared Drive Access for $User - Awaiting Additional Information"
$body = #"
<html>
<body style="font-family:calibri">
To $Requestor, $User,<br>
<br>
<br>
In order to proceed with your request for shared drive access, we require the server name and full path to the folder you need access to. If you do not already know this information, you will need to provide these instructions to someone that already has access to the folder that you need access to.<br>
<br>
1) Click the Start menu<br>
<br>
<img src="cid:image1.png"><br>
<img src="cid:image2.png"><br>
<img src="cid:image3.png"><br>
<br>
<br>
2) Navigate to "Computer"<br>
<br>
<img src="cid:image4.png"><br>
<br>
<br>
<br>
If you have any questions or need assistance with this process, please contact the Service Desk via one of the methods listed below.
<br>
<br>
Thank You,<br>
<br>
IT Service Desk<br>
</body>
</html>
"#
$att1 = new-object Net.Mail.Attachment ("T:\PS Scripts\Images\shareddrive1.png")
$att2 = new-object Net.Mail.Attachment ("T:\PS Scripts\Images\shareddrive2.png")
$att3 = new-object Net.Mail.Attachment ("T:\PS Scripts\Images\shareddrive3.png")
$att4 = new-object Net.Mail.Attachment ("T:\PS Scripts\Images\shareddrive4.png")
$att1.ContentId = "image1.png"
$att2.ContentId = "image2.png"
$att3.ContentId = "image3.png"
$att4.ContentId = "image4.png"
# $msg.Attachments.Add($att1)
# $msg.Attachments.Add($att2)
# $msg.Attachments.Add($att3)
# $msg.Attachments.Add($att4)
# create confirmation message
$ButtonType = [System.Windows.MessageBoxButton]::YesNo
$MessageIcon = [System.Windows.MessageBoxImage]::Warning
$MessageTitle = "Shared Drive Access Assistance"
$MessageBody = "The information you have entered is show below:`n`n`nTicket Number: $Ticket`n`nUser's Email Address: $UserID`n`nRequstor's Email Address: $RequestorID`n`n`nIf you would like to send the email, click Yes.`nOtherwise, click No."
$Result = [System.Windows.MessageBox]::Show($MessageBody,$MessageTitle,$ButtonType,$MessageIcon)
if ($Result -eq "No")
{
Exit-PSSession
}
else
# send email
{
Send-MailMessage -To "<$UserID>" -bcc "<$Username#dana.com>" -from "<itservicedesk#x.com>" -Subject $global:subject -SmtpServer "mailrelay.x.com" -BodyAsHtml -body $global:body
}
}
Function Clean-Memory {
Get-Variable |
Where-Object { $startupVariables -notcontains $_.Name } |
ForEach-Object {
try { Remove-Variable -Name "$($_.Name)" -Force -Scope "global" -ErrorAction SilentlyContinue -WarningAction SilentlyContinue}
catch { }
}
}
So the real question is how to embed a image into the HTML document from a attachment
CID aka Content ID will allow you to attach a image and then use that attached image in the document. Avoid using spaces in the Content ID name.
Send-MailMessage -To "Test#Test.com" -from "Test2#Test.com" -SmtpServer SMTP.TEST.NET -Subject "Hello" -BodyAsHtml -Body "<img src='cid:Test.png'>" -Port 25 -Attachments "C:\Users\Test\Test.png"
You are using
$att1 = new-object Net.Mail.Attachment ("T:\PS Scripts\Images\shareddrive1.png")
$att2 = new-object Net.Mail.Attachment ("T:\PS Scripts\Images\shareddrive2.png")
$att3 = new-object Net.Mail.Attachment ("T:\PS Scripts\Images\shareddrive3.png")
$att4 = new-object Net.Mail.Attachment ("T:\PS Scripts\Images\shareddrive4.png")
$att1.ContentId = "image1.png"
$att2.ContentId = "image2.png"
$att3.ContentId = "image3.png"
$att4.ContentId = "image4.png"
but when you send the mail you are not attaching those attachments
Send-MailMessage -To "<$UserID>" -bcc "<$Username#dana.com>" -from "<itservicedesk#x.com>" -Subject $global:subject -SmtpServer "mailrelay.x.com" -BodyAsHtml -body $global:body
You could stop using the Net.Mail.Attachment and instead do something like
$Body = #"
<html>
<body style="font-family:calibri">
<b>This is image 1</b>
<img src='cid:TEST1.png'>
<b>This is image 2</b>
<img src='cid:Test2.png'>
</body>
</html>
"#
Send-MailMessage -To "Test#Test.com" `
-from "Test2#Test.com" `
-SmtpServer Test.smtp.com `
-Subject "Hello" `
-BodyAsHtml -body $body `
-Attachments "C:\Test\TEST1.png", "C:\Test\TEST2.png"
You can embed the image into the HTML, therefore there are no extra files.
It is replacing
{img=file}
with data URL
{src="data:image/png;base64,[base64 encoded long string representing the image]}
There are many online converters that can convert your image file into the required code.
Just google for Image to Data URI converter.
Here is a simple example using MailKit. I had do download the packages from https://www.nuget.org/packages/MimeKit and https://www.nuget.org/packages/MailKit and unpack with 7Zip as PowerShell was struggling to install from the CLI. I got some ideas from here but wanted to add an example of an embedded image, https://adamtheautomator.com/powershell-email.
You can do all sorts with MimeKit and MailKit including modern Auth which is great. It's also supper fast and the Client and even a Connection can be used in a loop to send lots of messages. There is probably an upper bound on how long to use a connection without refreshing?
$mailServer = "domain-com.mail.protection.outlook.com"
$SMTP = New-Object MailKit.Net.Smtp.SmtpClient
Add-Type -Path ".\mailkit.3.3.0\netstandard2.0\MailKit.dll"
Add-Type -Path ".\mimekit.3.3.0\netstandard2.0\MimeKit.dll"
$SMTP.Connect($mailServer, 25, [MailKit.Security.SecureSocketOptions]::StartTls, $False)
$Message = New-Object MimeKit.MimeMessage
$message.From.Add("a.person#domain.com");
$message.To.Add("b.person#domain.com");
$message.Subject = "Some Subject";
$builder = [MimeKit.BodyBuilder]::new();
$image = $builder.LinkedResources.Add($path);
$image.ContentId = [MimeKit.Utils.MimeUtils]::GenerateMessageId();
$body = #"
<html>
<body style="font-family:calibri">
<b>This is image 1</b>
<img src='cid:$($image.ContentId)'>
</body>
</html>
"#
$builder.HtmlBody = $body
#Now we just need to set the message body and we're done
$message.Body = $builder.ToMessageBody();
$SMTP.Send($Message)
$SMTP.Disconnect($true)
$SMTP.Dispose()

Sending email with Powershell from csv list

I have a *.csv file with a few columns and I want to use the UsersTo and UsersCc list of users to send messages.
The problem is with $CcAddress as I have multiple email address once I format them I get the following :
error:
Send-MailMessage : An invalid character was found in the mail header: ','.
At C:\Discovery Scan Process\RSU notifications\Notifications.ps1:43 char:2
Send-MailMessage -Encoding UTF32 -to $ToAddress -Cc $CcAddress -from $FromAddre ...
+ CategoryInfo : InvalidType: (:) [Send-MailMessage], FormatException
+ FullyQualifiedErrorId : FormatException,Microsoft.PowerShell.Commands.SendMailMessage
CSV looks like this:
UsersTo,UsersCc,Domain
unknowTest.Test#test.com,"testOpen#new.com,Pvo.ovi#new.com",test.test
unknowNew.test#test.com,"testOpen#new.com,testOpen#new.com",new.test
unknowprod.mx#test.com,"testOpen#new.com,Evo.x#new.com",prod.test
unknowX.SS#test.com,"testOpen#new.com,Spekor1.#new.com",uat.test
Code:
$notificaitonList = import-csv .\'listOfUsers - Copy.csv'
$domains = 'test.test','prod.test'
foreach ($domain in $domains){
$FromAddress = "some#address"
$ToAddress = ($notificaitonList | Where-Object {$_.domain -like $domain}).UsersTo | foreach {"`"$_`""}
$Ccstr = (($notificaitonList | Where-Object {$_.domain -like "$domain"}).UsersCc).split(",")
$Ccstr = $Ccstr | foreach {"`"$_`""}
[string[]]$CcAddress= $Ccstr.Split(",") | foreach {"`"$_`""}
[string] $MessageSubject = "MessageSubject "
[string] $emailbody = "emailbody"
$SendingServer = "server"
Send-MailMessage -Encoding UTF32 -to $ToAddress -Cc $CcAddress -from $FromAddress -subject $MessageSubject -smtpServer $SendingServer -body $emailbody
}
You can simply split at the comma and let powershell handle the rest. Here is my test that confirmed this.
$tempfile = New-TemporaryFile
#'
UsersTo,UsersCc,Domain
some#email.com,"second#email.com,random#otherdomain.com",test.test
'# | Out-File $tempfile -Encoding utf8
Import-CSV $tempfile | foreach {
$mailparams = #{
SMTPServer = 'myexchangeserver'
From = "noreply#email.com"
To = $_.usersto
CC = $_.userscc -split ','
Subject = 'test email'
Body = 'test body'
}
Send-MailMessage #mailparams
}

Powershell filedownload from https

I am trying to download several files using Powershell and its Invoke-WebRequest method.
I'm basically looping through several filenames (I know that they are available on the server) and download them.
My problem is that my script works for the first file and fails for every file that follows. When I open one of the later files (.csv`s) there is just some html code in it).
I already read a lot about passing session cookings but I am not sure if this is my problem or how I can do that.
My script so far looks like this:
$httpsUser = 'XXX'
$httpsPass = 'YYY'
foreach ($instrument in 'ivv','ijh','ijr','iwm') {
$Source = 'https://***', `
$instrument, '-en_us.csv' -join ""
$Target = 'C:\User\', `
$instrument, '-en_us.csv' -join ""
$uri = New-Object “System.Uri” “$Source”
$WebClient = [System.Net.HttpWebRequest]::Create($uri)
$webclient.Proxy.Credentials =
[System.Net.CredentialCache]::DefaultNetworkCredentials
$webclient.Credentials =
New-Object System.Net.NetworkCredential($httpsUser,$httpsPass)
Invoke-WebRequest -Uri $Source -OutFile $Target
}
Thank you all and let me know what you think :)
It seems like you don't using the HttpWebRequest you created to download the file. Anyway, I would recommend using System.Net.WebClient:
$wc = New-Object System.Net.WebClient
$wc.Credentials = New-Object System.Net.NetworkCredential($httpsUser,$httpsPass)
$wc.DownloadFile($Source, $target)
Try using webclient methods DownloadFile(src,dst). Should be something like this:
$httpsUser = 'XXX'
$httpsPass = 'YYY'
foreach ($instrument in 'ivv','ijh','ijr','iwm') {
$Source = 'https://***', `
$instrument, '-en_us.csv' -join ""
$Target = 'C:\User\', `
$instrument, '-en_us.csv' -join ""
$webclient = New-Object -TypeName Net.WebClient
$webclient.Encoding = [System.Text.Encoding]::UTF8
$webclient.UseDefaultCredentials = $true
$webclient.Proxy.Credentials = New-Object System.Net.NetworkCredential($httpsUser,$httpsPass)
$webclient.DownloadFile($Source,$Target)
}
Thank you all for your answers. I figured out now that the site generates a security token while logging in. This token needs to be passed in every webrequest. I was not yet able to figure out how to do that with powershell but know that perl has a build in function (called $merch) for exactly this problem.
To solve my problem I had to automatize the IE :( (I know this is not the most sophisticated way but right now it is the quickest solution. If anyone is interested here is the code for that:
$ie = new-object -ComObject 'InternetExplorer.Application'
$requestUri = 'https://www.trololo.com'
$userIdFragment = "userName";
$passwordIdFragment = "password";
$buttonIdFragment = "submitLogin";
$ie.visible = 'false'
$ie.navigate($requestUri)
while($ie.Busy) { Start-Sleep -Milliseconds 100 }
$doc1 = $ie.Document
$doc1.getElementsByTagName("input") | % {
if ($_.id -ne $null){
if ($_.id.Contains($buttonIdFragment)) { $btn = $_ }
if ($_.id.Contains($passwordIdFragment)) { $pwd = $_ }
if ($_.id.Contains($userIdFragment)) { $user = $_ }
}
}
$user.value = "XXXX"
$pwd.value = "YYYY
$btn.disabled = $false
$btn.click()
while($ie.Busy) { Start-Sleep -Milliseconds 5000 }
$ie.navigate($requestUri)
while($ie.Busy) { Start-Sleep -Milliseconds 200 }
$doc1 = $ie.Document
$link = $doc1.getElementsByTagName("a") | where-object {$_.href -match "Your String"}
$link.click()
Start-Sleep -Milliseconds 1000
$wshell = new-object -com wscript.shell
$wshell.appactivate("Internet Explorer")
$wshell.sendkeys("%s")

Return an array from a function in PowerShell 3.0

I am trying to have PowerShell 3.0 return an array from a function. Unfortunately this is not very well documented as I have spent the past two days scouring Google for examples on this. I am this close to rewriting the entire script in C# and calling it a day.
The script checks a set of URLS which are contained in a variable. The function in question gets the list of URL's from an array and loops through the array adding the HTTP status code to a new array. The function does all of the above, however it does not return the array. Here is the function in question:
function URLCheck ($URLStatusCode)
{
foreach($uri in $URLStatusCode )
{
$result = #()
$time = try
{
$request = $null
## Check response time of requested URI.
$result1 = Measure-Command { $request = Invoke-WebRequest -Uri $uri}
$result1.TotalMilliseconds
}
catch
{
<# If request generates exception such as 401, 404 302 etc,
pull status code and add to array that will be emailed to users #>
$request = $_.exception.response
$time = -1
}
$result += [PSCustomObject] #{
Time = Get-Date;
Uri = $uri;
StatusCode = [int] $request.StatusCode;
StatusDescription = $request.StatusDescription;
ResponseLength = $request.RawContentLength;
TimeTaken = $time;
}
}
return $result
}
I call the like this:
URLCheck $LinuxNonProdURLList
$result
I also printed the contents of $result after the execution and I notice that it is empty. However if I were to put the return statement in the foreach loop, it does send the information to the console.
Any help would be greatly appreciated.
After a little more troubleshooting I figured out that the array $result was only local to the function. I declared the array outside of the foreach loop and that fixed the error. Here is the updated code:
function URLCheck ($URLStatusCode)
{
$result = #()
foreach($uri in $URLStatusCode )
{
$time = try
{
$request = $null
## Check response time of requested URI.
$result1 = Measure-Command { $request = Invoke-WebRequest -Uri $uri}
$result1.TotalMilliseconds
}
catch
{
<# If request generates exception such as 401, 404 302 etc,
pull status code and add to array that will be emailed to users #>
$request = $_.exception.response
$time = -1
}
$result += [PSCustomObject] #{
Time = Get-Date;
Uri = $uri;
StatusCode = [int] $request.StatusCode;
StatusDescription = $request.StatusDescription;
ResponseLength = $request.RawContentLength;
TimeTaken = $time;
}
}
return $result
}

Resources