WebClient.Downloadfile API is not working in powershell - powershell-2.0

(new-object System.Net.WebClient).Downloadfile("https://www.dropbox.com/sh/tsyz48qg0rq3smz/QAstBLgPgN/version.txt", "C:\Users\Brangle\Desktop\version.txt") API download invalid data.
version.txt file need to download. But actually it is downloading some xml file contains in version.txt on destination location
Thanks in advance

You are trying to download the dropbox page which presents your file in a nice dropbox-themed html. You need to extract the real url and can do so using the following code:
$wc = New-Object system.net.webclient;
$s = $wc.downloadString("https://www.dropbox.com/sh/tsyz48qg0rq3smz/QAstBLgPgN/version.txt");
$r = [regex]::matches($s, "https://.*token_hash.*(?=`")");
$realURL = $r[$r.count-1].Value;
$wc.Downloadfile($realURL, "U:\version.txt");
The regex part looks for a url starting https://, has a string token_hash in the middle and ends one character before double quotes character ". The line in question is:
FilePreview.init_text("https://dl.dropboxusercontent.com/sh/tsyz48qg0rq3smz/QAstBLgPgN/version.txt?token_hash=AAEGxMpsE-T4xodBPd3A6uPTCr0uqh7h4B2YUSmTDJHmjg", 0, null, 0)
Hope this helps.

Here is the function:
function download-dropbox($Url, $FilePath) {
$wc = New-Object system.net.webclient
$req = [System.Net.HttpWebRequest]::Create($Url)
$req.CookieContainer = New-Object System.Net.CookieContainer
$res = $req.GetResponse()
$cookies = $res.Cookies | % { $_.ToString()}
$cookies = $cookies -join '; '
$wc.Headers.Add([System.Net.HttpRequestHeader]::Cookie, $cookies)
$newurl = $url + '?dl=1'
mkdir (Split-Path $FilePath) -force -ea 0 | out-null
$wc.downloadFile($newurl, $tempFile)
}

Re: LogMeIn - theyuse a cookie base authentication so you can't use the previous code. Try this, it gets a cookie from the first response and then uses that to download using webclient:
$url = "https://secure.logmein.com/fileshare.asp?ticket=01_L5vwmOrmsS3mnxPO01f5FRbWUwVKlfheJ5HsfpTV"
$wc = New-Object system.net.webclient
$req = [System.Net.HttpWebRequest]::Create($url)
$req.CookieContainer = New-Object System.Net.CookieContainer
$res = $req.GetResponse()
$cookie = $res.Cookies.Name + "=" + $res.Cookies.Value
$wc.Headers.Add([System.Net.HttpRequestHeader]::Cookie, $cookie)
$newurl = $url + "`&download=1"
$wc.downloadFile($newurl, "c:\temp\temp.zip")

Related

How to access Zoom recordings with password using oauth via api?

We have an app which is not in public marketplace of Zoom right now but we use it to import Zoom meeting recordings of our clients on our platform. Normal getting of videos from Zoom api is working fine, but when we try to import videos which have a password on them, we are able to get to the download_url by hitting the <meetings/{meetingId}/recordings> end point but when we hit the download url using with the authorization (outh) token, we get Forbidden response.
The documentation only says on https://marketplace.zoom.us/docs/api-reference/zoom-api/cloud-recording/recordingget to use a JWT token for downloading the recording but how do we proceed if we have an Oauth app?
as noted by dark light, you cannot use oauth to download the files... but you can use jwt and its kinda simple....
#pre-reqs, PSZoom powershell module, an admin account on zoom, your oauth set and your JWT Auth set.
#update this to match your environment - you will need a JWT token, and they expire regularly,
#you will need your from and to dates, your download location,
#and to choose if you want auto delete (change no to yes AND uncomment the remove section on line 38
#autodelete is untested, be careful
import-module PSZoom
$Global:ZoomApiKey = 'YOUR API KEY'
$Global:ZoomApiSecret = 'YOUR API SECRET'
$base = "C:\working\Testing\"
$fromfile1 = "C:\working\zoomusers_$((Get-Date).ToString('MM-dd-yyyy')).csv"
$fromfile2 = "c:\working\rcdngs_$((Get-Date).ToString('MM-dd-yyyy')).csv"
$fromfile3 = "c:\working\DWNLD_$((Get-Date).ToString('MM-dd-yyyy')).csv"
$downlog = "c:\working\ZoomDownloads_$((Get-Date).ToString('MM-dd-yyyy')).log"
$deletelog = "c:\working\ZoomDelete_$((Get-Date).ToString('MM-dd-yyyy')).log"
$exportPath = 'c:\working\files\'
$Users = Import-Csv -Path $FromFile1
$MIDS = Import-Csv -Path $fromfile2
$Links = Import-Csv -Path $fromfile3
$jwt='YOUR VERY LONG JWT TOKEN'
$AToken='/?access_token='
$autodelete= 'NO'
Get-ZoomUsers -AllPages -status active | Where-Object {$_.type -eq "2"} |select id,first_name,last_name,email,pmi | export-csv $fromfile1 -NoTypeInformation
foreach ($user in $Users) {
Get-zoomRecordings -userId $user.email -From 2021-09-20 -PageSize 300 -To 2021-09-24 |select -ExpandProperty meetings| export-csv $fromfile2 -NoTypeInformation
foreach ($MID in $MIDS) {
Get-ZoomMeetingRecordings -MeetingId $MID.uuid | select -property topic,start_time -ExpandProperty recording_files| select -property topic,start_time,meeting_id,download_url| export-csv $fromfile3 -Append -NoTypeInformation}
}
foreach ($Link in $Links) {
$MTD = $Link.start_time.substring(0,10)
$path = ($base + $Link.topic)
If(!(test-path $path))
{
New-Item -ItemType Directory -Force -Path $path
}
$uri= ($link.download_url + $AToken + $jwt)
$inc = 1
$file=($path + "\" + $mtd + "-" + $inc + ".mp4")
If(!(test-path $file)) {
$wc=New-Object System.Net.WebClient
$wc.DownloadFile($uri,$file)
echo "downloading $file" | out-file $downlog -append}
else {
$inc=$inc+1
$file=($path + "\" + $mtd + "-" + $inc + ".mp4")
$wc=New-Object System.Net.WebClient
$wc.DownloadFile($uri,$file)
echo "downloading $file" | out-file $downlog -append}
if($autodelete -eq 'YES'){
#Remove-ZoomMeetingRecordings -MeetingId $link.meeting_id -Action delete
echo ($link.topic + " on " + $MTD + " was succesfully delted on $((Get-Date).ToString('MM-dd-yyyy_hh-mm-ss'))") | out-file $deletelog -append
}
}
So I found out the issue in Zoom discussions and currently as on 22 April 2020, it is not possible. You might need to use notification feature for that as of now - https://devforum.zoom.us/t/best-way-to-download-recording-files-programmatically/7386/20

Get Workitems from VSTS

I'm having some troubles to get all workitems/tasks and who is an assignee.
According to this answer is possible get the tasks using the report work, but is retrieving absolutely all.
https://xxx.visualstudio.com/{project}/_apis/wit/reporting/workitemrevisions?includeLatestOnly=true&api-version=5.0-preview.2
Is possible retrieve the id, title and who is an assignee?
You could add parameter fields=System.Id,System.Title,System.AssignedTo in the api:
GET https://{accountName}.visualstudio.com/{project}/_apis/wit/reporting/workitemrevisions?fields=System.Id,System.Title,System.AssignedTo&includeLatestOnly=true&api-version=5.0-preview.2
You can use below PowerShell script to call the REST API and retrieve the work item id, title and assignee and any other elements you needed.
Alternatively you can export the work item list to a *.csv file.
Param(
[string]$collectionurl = "https://xxx.visualstudio.com",
[string]$project = "ProjectName",
[string]$user = "username",
[string]$token = "Password/PAT"
)
# Base64-encodes the Personal Access Token (PAT) appropriately
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $user,$token)))
$baseUrl = "$collectionurl/$project/_apis/wit/reporting/workitemrevisions?includeLatestOnly=true&api-version=5.0-preview.2"
$response = (Invoke-RestMethod -Uri $baseUrl -Method Get -UseDefaultCredential -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)}).values
$wits = $response | where({$_.fields.'System.WorkItemType' -eq 'Task'}) # Only retrieve Tasks
$witrevisions = #()
foreach($wit in $wits){
$customObject = new-object PSObject -property #{
"WitID" = $wit.fields.'System.Id'
"rev" = $wit.fields.'System.Rev'
"Title" = $wit.fields.'System.Title'
"AssignedTo" = $wit.fields.'System.AssignedTo'
"ChangedDate" = $wit.fields.'System.ChangedDate'
"ChangedBy" = $wit.fields.'System.ChangedBy'
"WorkItemType" = $wit.fields.'System.WorkItemType'
}
$witrevisions += $customObject
}
$witrevisions | Select-Object `
WitID,
rev,
Title,
AssignedTo,
ChangedDate,
ChangedBy,
WorkItemType #| export-csv -Path D:\temp\WIT.csv -NoTypeInformation

I need to upload a file and take the file name from the address

The code below will open an initial window where you need to input the number of files you are uploading, after that in the next window you select the files to be uploaded. Here you will see the full path of the file you have uploaded but I need only the file name that is selected, NOT the full path that is showing .e.g. System.Windows.Forms.TextBox, Text: C:\Users\Gourav Bid\Desktop\pwd.txt. I only need to take out the pwd.txt.
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
###### Initial Form Design ######
$objForm = New-Object System.Windows.Forms.Form
$objForm.Text = "File Upload"
$objForm.Size = New-Object System.Drawing.Size(350,200)
$objForm.StartPosition = "CenterScreen"
$objForm.KeyPreview = $True
$objForm.Add_KeyDown({if ($_.KeyCode -eq "Escape")
{$objForm.Close()}})
###### Label & TextBox Design ######
$objLabel = New-Object System.Windows.Forms.Label
$objLabel.Location = New-Object System.Drawing.Point(40,20)
$objLabel.Size = New-Object System.Drawing.Size(280,20)
$objLabel.Text = "Enter The Number Of Artefacts File Required :"
$objForm.Controls.Add($objLabel)
$objTextBox = New-Object System.Windows.Forms.TextBox
$objTextBox.Location = New-Object System.Drawing.Point(40,40)
$objTextBox.Size = New-Object System.Drawing.Size(260,20)
$objForm.Controls.Add($objTextBox)
###### Operation Buttons & Cancel Button Design ######
$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Point(40,80)
$OKButton.Size = New-Object System.Drawing.Size(70,20)
$OKButton.Text = "OK"
$OKButton.Add_Click{(Button), $objForm.Close()}
$objForm.Controls.Add($OKButton)
$CancelButton = New-Object System.Windows.Forms.Button
$CancelButton.Location = New-Object System.Drawing.Point(120,80)
$CancelButton.Size = New-Object System.Drawing.Size(70,20)
$CancelButton.Text = "Cancel"
$CancelButton.Add_Click({$objForm.Close()})
$objForm.Controls.Add($CancelButton)
function Button {
[int]$artefacts = $objTextBox.Text
$d = 100 + $artefacts*30
$OKLoc = $d-70
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
$Form1 = New-Object System.Windows.Forms.Form
$Form1.Size = New-Object System.Drawing.Size(400,$d)
$Form1.Text = "Select Artefacts Files"
$Form1.StartPosition = "CenterScreen"
$Form1.AutoScroll = $True
$Form1.KeyPreview = $True
############################################## Buttons ##############################################
$OKButton1 = New-Object System.Windows.Forms.Button
$OKButton1.Location = New-Object System.Drawing.Size(20,$OKLoc)
$OKButton1.Size = New-Object System.Drawing.Size(75,23)
$OKButton1.Text = "OK"
$OKButton1.Add_Click({$x=$objTextBox.Text;$Form1.Close();{split};Write-Host "$arte1"})
$Form1.Controls.Add($OKButton1)
$CancelButton1 = New-Object System.Windows.Forms.Button
$CancelButton1.Location = New-Object System.Drawing.Size(150,$OKLoc)
$CancelButton1.Size = New-Object System.Drawing.Size(75,23)
$CancelButton1.Text = "Cancel"
$CancelButton1.Add_Click({$Form1.Close()})
$Form1.Controls.Add($CancelButton1)
############################################## Buttons ##############################################
if ($artefacts -eq 1) {
$Label = New-Object System.Windows.Forms.Label
$Label.Location = New-Object System.Drawing.Size(15,20)
$Label.Size = New-Object System.Drawing.Size(50,20)
$Label.Text = "File 1"
$Form1.Controls.Add($Label)
$arte = New-Object System.Windows.Forms.TextBox
$arte.Location = New-Object System.Drawing.Size(80,15)
$arte.Size = New-Object System.Drawing.Size(280,20)
$Form1.Controls.Add($arte)
}
$Button = New-Object System.Windows.Forms.Button
$Button.Location = New-Object System.Drawing.Size(270,$OKLoc)
$Button.Size = New-Object System.Drawing.Size(75,23)
$Button.Text = "Browse"
$Button.Add_Click({Button})
#Write-Host $test
$Form1.Controls.Add($Button)
#####################################################Function Button#####################################################
function Read-OpenFileDialog([string]$WindowTitle, [string]$InitialDirectory, [string]$Filter = "All files (*.*)|*.*", [switch]$AllowMultiSelect) {
Add-Type -AssemblyName System.Windows.Forms
$openFileDialog = New-Object System.Windows.Forms.OpenFileDialog
$openFileDialog.Title = $WindowTitle
if (![string]::IsNullOrWhiteSpace($InitialDirectory)) {
$openFileDialog.InitialDirectory = $InitialDirectory
}
$openFileDialog.Filter = $Filter
if ($AllowMultiSelect) {
$openFileDialog.MultiSelect = $true
}
$openFileDialog.ShowHelp = $true
# Without this line the ShowDialog() function may hang depending on system configuration and running from console vs. ISE.
$openFileDialog.ShowDialog() > $null
if ($AllowMultiSelect) {
return $openFileDialog.Filenames
} else {
return $openFileDialog.Filename
}
}
function Button {
for ($a=0;$a -lt $artefacts;$a++) {
$b = $a+1
$arte[$a].Text = Read-OpenFileDialog -WindowTitle "Select File $b" -InitialDirectory "C:\" -Filter "ALL files (*.*)|*.*"
}
Write-Host "$arte"
$arte1=$arte
}
function Split {
$arte1.split('\')[1]#.split(',')[0]
Write-Host "$arte1"
}
#####################################################Function Button
if ($artefacts -gt 1) {
$b = 20
$c = 15
for ($a=0;$a -lt $artefacts;$a++) {
$d = $a + 1
$Label = New-Object System.Windows.Forms.Label
$Label.Location = New-Object System.Drawing.Size(15,$b)
$Label.Size = New-Object System.Drawing.Size(50,20)
$Label.Text = "File $d"
$Form1.Controls.Add($Label)
[Array]$arte += New-Object System.Windows.Forms.TextBox
$arte[$a].Location = New-Object System.Drawing.Size(80,$c)
$arte[$a].Size = New-Object System.Drawing.Size(280,20)
$Form1.Controls.Add($arte[$a])
$b = $b+30
$c = $c+30
}
}
$Form1.Add_Shown({$Form1.Activate()})
[void] $Form1.ShowDialog()
}
$objForm.Topmost = $false
$objForm.Add_Shown({$objForm.Activate()})
[void] $objForm.ShowDialog()
You can use Split-Path if you only need the file name:
Split-Path "C:\Users\Gourav Bid\Desktop\pwd.txt" -Leaf
outputs
pwd.txt
In your example, you can replace
Write-Host "$arte"
with
$arte | % { Write-Host (Split-Path $_.Text -Leaf) }
it will iterate on $arte, and extract the filename from the .Text property.
The output you get is the whole TextBox object, you need to specify which property you want, in this case .Text.

Powershell Parsing CSV file that contains comma

Consider that CSV file:
Node Name,Client Name,Job Directory,Policy Name
server1,test.domain.com,"vmware:/?filter= VMHostName AnyOf "server2.domain.com", "server3.domain.com"",TEST
My code:
$events = Import-Csv "C:\file.csv" | foreach {
New-Object PSObject -prop #{
Server = $_.{Node Name};
Client = $_.{Client Name};
{JobDirectory/Script} = $_.{Job Directory};
Policy = $_.{Policy Name};
}
}
I have some problems when I try to parse the third field. I am not sure if its because the comma, or the double quote.
This is the object I would like to have:
Node Name : server1
Client Name : test.domain.com
JobDirectory/Script : vmware:/?filter= VMHostName AnyOf "server2.domain.com", "server3.domain.com"
Policy Name : TEST
Can someone help me?
Ok, so the easiest way to approach this is to read the file in with Get-Content and then split each line where the commas are not inside quotes. I borrowed the regex from this solution for this.
Using your current input data I would do something like this
$filedata = Get-Content C:\temp\test.csv
$asObject = ForEach($singlerow in ($filedata | Select-Object -Skip 1)){
$props = #{}
$singlerow = $singlerow -split ',(?=(?:[^"]*"[^"]*")*[^"]*$)'
[pscustomobject][ordered]#{
Server = $singlerow[0]
Client = $singlerow[1]
"JobDirectory/Script" = $singlerow[2]
Policy = $singlerow[3]
}
}
Sample Output from $asObject | Format-List
Server : server1
Client : test.domain.com
JobDirectory/Script : "vmware:/?filter= VMHostName AnyOf "server2.domain.com", "server3.domain.com""
Policy : TEST
Another way using your starting code
$obj = gc c:\temp\test.csv |
% { $_ -replace '"(\b[^"]*\b)"','$1' } |
convertfrom-csv | % { [pscustomobject][ordered] #{
Server = $_.{Node Name}
Client = $_.{Client Name}
{JobDirectory/Script} = $_.{Job Directory}
Policy = $_.{Policy Name} }
}

Parsing PDF by line

I've been able to parse a PDF by page multiple ways, the latest being this (not my code):
$reader = New-Object iTextSharp.text.pdf.pdfreader -ArgumentList "oldy.pdf"
for ($page = 1; $page -le $reader.NumberOfPages; $page++)
{
$strategy = new-object 'iTextSharp.text.pdf.parser.SimpleTextExtractionStrategy'
$currentText = [iTextSharp.text.pdf.parser.PdfTextExtractor]::GetTextFromPage($reader, $page, $strategy);
[string[]]$Text += [system.text.Encoding]::UTF8.GetString([System.Text.ASCIIEncoding]::Convert( [system.text.encoding]::default, [system.text.encoding]::UTF8, [system.text.Encoding]::Default.GetBytes($currentText)));
}
I found a post here that suggested using LocationTextExtractionStrategy instead and splitting each line out by '\n'
However, I will admit that the .NET code here is confusing me and i'm not sure how to modify it to parse by string.
Can anyone help?
thanks.
Only a first experiment, but it works as expected:
# Download http://sourceforge.net/projects/itextsharp/
Add-Type -Path itextsharp.dll
$reader = New-Object iTextSharp.text.pdf.pdfreader -ArgumentList MyFile.pdf
for ($page = 1; $page -le $reader.NumberOfPages; $page++)
{
# extract a page and split it into lines
$text = [iTextSharp.text.pdf.parser.PdfTextExtractor]::GetTextFromPage($reader,$page).Split([char]0x000A)
Write-Host "Page $($page) contains $($text.Length) lines. This is line 5:"
Write-Host $text[4]
#foreach ($line in $text)
#{
# any tasks
#}
}
$reader.Close()

Resources