Invoke-WebRequest : Cannot bind parameter 'Uri'. Cannot convert the "#{ - arrays

Would like to retrive innerText of a P html element for all the URLS i got listed in a text file. I'm rather newbie for this, but thought i can solve it. Atm. i've failed, as i can't handle how i shall pass each array items for the loop correctly:
$theURLS = Import-CSV linkek_alatt.txt
$item = New-Object System.Collections.ArrayList
for ($i=0; $i -le $theURLS.length;$i++)
{
foreach($item in $theURLS)
{
$Site = Invoke-WebRequest -URI $item
$Igotit = $Site.AllElements | Where-Object {$_.tagName -eq "P"} |
Select -First 1 -Skip 3 -ExpandProperty innerText
$Igotit
}
}
> FilteredContent.txt
The Filtered file shall contain the informations.
For now, i receive a "Invoke-WebRequest : Cannot bind parameter 'Uri'. Cannot convert the "#{" error and i see repeated first URL and a random - the one for next step - one in the error message. Any ideas welcome.
Best regards,
Geza

Per the comments, the issue occurs because you are using Import-CSV here:
$theURLS = Import-CSV linkek_alatt.txt
This attempts to create a PowerShell object by using the first line of text in the file as headers.
If your .txt file is not a .csv file and you want to return an array of strings, instead use Get-Content:
$theURLS = Get-Content linkek_alatt.txt
If your file is a CSV file, then you'll need to reference the property name that matches the header of the URLs when using it in Invoke-WebRequest. E.g if it had a header of "URL":
Invoke-WebRequest -URI $item.URL
But I suspect this is not the case for you.

Related

Possible to have different array names when write-output PSCustomObject]#{ in my function?

Is there any way to name the variable for an array something in my Catch { and then have another array name for the array inside Try/script part of my function?
Cause when i try doing like this $computerObject = [PSCustomObject]#{
and then doing Write-Output $computerArray i can only get either my variables inside Try/script array being displayed inside Powershell window. Or only get the $error message from my Catch.. Is there any way to name each array something so i can do like below.
write-host "Results"
Write-Output $computerArray - display my first array here
write-host "Failed: computerlist" -foregroundcolor red
Write-Output $computerArray2 - display $error computers here. $error should just include computers who did not answer to ping and other stuff from my invoke-command computerlist.txt
The only true answer to why i need this separately is that sometimes i want my array in a CSV file. And sometimes i just want to copy info directly from Powershell window. And then its more practical to have failed computers separated and not in the same array output.
This function (as mentioned in comments) doesn't leverage the CIM cmdlets parallel capabilities, would recommend some tweaks to it but to answer the actual question, how can you "split" the output between success and fail:
The function as-is, doesn't require any modification to achieve this, it's try and catch blocks are outputting objects with the same properties and luckily one of those properties is Error and it's value is a boolean so you can simply first query all the computers and then split the result using .Where with Split mode.
The code would be like this:
$computers = 'computer1', 'computer2', ....
$computerArray = foreach($computer in $computers) {
Get-ComputerInformation -ComputerName $computer
}
# now we can split between FAIL and SUCCESS
$fail, $success = $computerArray.Where({ $_.Error }, 'Split')
$success | Export-Csv path\to\success.csv -NoTypeInformation
$fail | Export-Csv path\to\fail.csv -NoTypeInformation

Trying to output a custom powershell object where I can align each line of two different variables containing Category:Description

I'm trying to do an network access control audit by grabbing a user's AD groups, their descriptions and then output them in a way shown by this example:
[User]
#[1]Groups : #[1]GroupDescription
#[2]...
#[3]...
Below is what I have at the moment.
$UserGroups = #{
User = Read-Host -Prompt "What user do You want to look up Access for?"
Groups = (Get-ADUser $User -Properties MemberOf).MemberOf
GroupsDescriptions = (Get-ADUser $User -Properties MemberOf).MemberOf | % {(Get-ADGroup $_ -Properties *).description}
}
$Object = New-Object psobject -Property $UserGroups
$Object | format-table | Export-Csv c:\tmp\test.csv
Though the output is very strange. I don't understand it. Below is a result of Get-Content C:tmp\test.csv
#TYPE Microsoft.PowerShell.Commands.Internal.Format.FormatStartData
"ClassId2e4f51ef21dd47e99d3c952918aff9cd","pageHeaderEntry","pageFooterEntry","autosizeInfo","shapeInfo","groupingEntry"
"033ecb2bc07a4d43b5ef94ed5a35d280",,,,"Microsoft.PowerShell.Commands.Internal.Format.TableHeaderInfo",
"9e210fe47d09416682b841769c78b8a3",,,,,
"27c87ef9bbda4f709f6b4002fa4af63c",,,,,
"4ec4f0187cb04f4cb6973460dfe252df",,,,,
"cf522b78d86c486691226b40aa69e95c",,,,,
I have tried outputting to a .txt file using Out-file, but I always get each property cut off with a ... at the end. I've used the -Autosize and -Expand when formatting the data before piping it to the export line.
Any Suggestions or advice would be extremely helpful.
Things I'll be Looking at later
Go through each line in PowerShell object and extract variables
Powershell & ActiveDirectory - trying to output users in a group and their membership
Out-file crops my text when trying to output a table
Thanks!
As stated, only ever use Format-* cmdlets to produce for-display output, never for outputting data that must be processed programmatically later. What Format-Table outputs are objects representing formatting instructions, and it is their properties that ended up in your CSV file - see this answer for more information.
In order to include collections (arrays) in CSV output, you must convert them to a single string, using a self-chosen separator. Otherwise, Export-Csv simply calls .ToString() on the collection object itself, which yields the collection's type name, and no information about its elements.
Therefore, use something like the following, which uses ', ' as the separator string to represent the group names and descriptions in a single column each:
$UserGroups = [pscustomobject] #{
User = ($user = Read-Host -Prompt "What user do You want to look up Access for?")
Groups = ($groups = (Get-ADUser $User -Properties MemberOf).MemberOf) -join ', '
GroupsDescriptions = (
$groups | ForEach-Object { (Get-ADGroup $_ -Properties *).Description }
) -join ', '
}
$UserGroups | Export-Csv c:\tmp\test.csv
Note:
[pscustomobject] #{ ... } is used to directly construct a custom object, which is syntactic sugar available since PowerShell v3 that is simpler and more efficient than a New-Object call.
In order to use the result from your Read-Host call in later properties of your object definition, you must cache it in aux. variable $user (note that enclosing the assignment in (...) passes its value through.
Similarly, the result of the Get-ADUser call is cached in aux. variable $groups, so that it doesn't have to be repeated in the GroupsDescriptions value.
However, as zett42 points out, it may be cleaner to make the $user = ... and $groups = ... assignments separate statements and place them before the object construction.
The problem is that you pipe to Format-Table before you pipe to Export-Csv. Only use Format-Table for displaying things on screen. The fix is to just remove that.
$Object | Export-Csv c:\tmp\test.csv
Thanks to this post Here and mklement0. I was able to figure out the formatting portion of this problem.
Now I have the remaining code that exports it exactly as intended.
$user= Read-Host -Prompt "What user bonehead?"
$object = Get-ADPrincipalGroupMembership $user
$Table = $object | ForEach-Object {
[pscustomobject] #{
Groups = $_.Name
GroupDesc = (Get-ADGroup $_ -Properties *).Description
GroupOwner = (Get-ADGroup $_ -Properties *).Info
}
}
$Table | Export-csv -NoTypeInformation c:\tmp\test.csv
The -NoTypeInformation helps eliminate the header on the .csv files and the piped Group info through the ForEach-Object cmdlet helped insure every object had it's own row in excel.

Extract partial line of text from text file

I need to keep the first 5 characters from data being pulled from a text file.
Data looks like this:
S1831KWT0081
S2004KWT0083
S2351KWT0085
S0054KWT0087
Results should looks like this:
S1831
S2004
S2351
S0054
I can get it working when setting the variable within PowerShell:
PS> $a = "S1831KWT0081"
PS> $a.Substring(0, $a.IndexOf('K'))
S1831
but I'm stuck when trying to pull from a text file.
To solve this, you will need to parse the text file on a line-by-line basis. Basically treating each line as a value in an array.
Get-Content location.txt | foreach { $_.Substring(0, $_.IndexOf('K')) }
Another option would be a regular expression replacement:
(Get-Content 'C:\path\to\input.txt') -replace '^(.{5}).*', '$1'
That would also allow you more specific matches, e.g. like this:
$re = '^([a-z]\d{4}).*'
(Get-Content 'C:\path\to\input.txt') -match $re -replace $re, '$1'
Just to show there always is more than one PoSh way ;-)
gc .\input.txt|%{$_.split('K')[0]}
Or the more verbose version
Get-Content .\input.txt |
ForEach-Object { $_.split('K')[0] }

Powershell: Assign properties to existing array

I have a single dimensional array that I get from either a get-content command or from multi-line text box input. I want to assign a property to the entries in this array, then add more properties to use later in my script.
Something like:
$items = new-object psobject
$items | add-member -membertype NoteProperty –name Name –value NotSet
$items | add-member -membertype NoteProperty –name Percent –value NotSet
$names = #($textboxInputText.Lines)
$names | % { $items | Add-Member noteproperty $_.Name $temp.($_.Name) }
foreach ($item in $items)
{
$percent = {script block}
$item.percent = $percent
}
I know this is broken code, but I wanted to give an example of where I was headed. I've searched far and wide but haven't been able to find exactly what I was looking for.
EDIT:
Code Goal: Get input from a text box or text file (single line entries). Have those entries be assigned to the "name" property, then add a second property to the array (Percent) that will need to be filled in with another block of code.
EDIT 2:
Collection is being used in the following code:
foreach ($item in $collection) {
$psConsoleFile = "PATH TO FILE.pc1"
$variable1 = "something"
$variable2 = "something else"
$command = ".`"Command1 $item.name | Command2 -Switch $variable1 -Switch2 $variable2`""
$OutputScriptBlock = "powershell.exe -PSConsoleFile $psConsoleFile -command $command"
}
The output of this is as follows:
powershell.exe -PSConsoleFile "PATH TO FILE.psc1" -command ."Command1 #{Name=name1; Percentage=}.name | Command2 -Switch1 something"
Why is the code outputting the full row instead of the name?
Also, I'm using PS 4.0 for all implementations of this script.
Ok, I see the problem here. So you have an array of Strings that you got either from a multi-line text box form object, or from a text document with the Get-Content command, but what you really want is an array of PSObjects.
A string object can not have additional properties added to it like you want (well, not conventionally, let's just not go there because you won't be happy with where things end up, trust me on this one). Instead let's take that array of strings, and for each string create a PSObject for it like you want. You will want a ForEach-Object loop for this to be simple. Either way you will want to pipe your input (either the textbox or the get-content command) to a ForEach loop, and you can assign the whole thing to a variable that will collect all of the objects to be worked with later (to update the Percent property). Something like this should accomplish what you want:
[Array]$Collection = $textboxInputText.Lines | ForEach{
New-Object PSObject -Property #{
'Name' = $_
'Percentage' = $null
}
}
I specified $Collection as the type [Array] so that if you wanted to index into it later there wouldn't be any issues should your input only be a single item. Then if you want to update the percentages you can do that by running $Collection through a ForEach loop (either inline or not)
$Collection | ForEach{ $_.Percentage = {Script Block} }
or
ForEach($Item in $Collection){
$Item.Percentage = {Script Block}
}
Now, things to note here... You are not going to be able to just assign $Collection back to your textbox. You could probably assign $Collection.Name, but that may require a newer version of PS since I don't know how backwards compatible that is. If you use a Get-Content command instead of referencing the textbox, simply change $textboxInputText.Lines | ForEach{ to Get-Content "C:\Path\To\File.txt" | ForEach{ and you should be all set.
Edit: Ok, the problem you have now isn't with the object but with how you're trying to expand a property of it within a double quotes. To access the name you would have to create a sub expression within the double quotes by wrapping $Item.Name within $(). So that line for you would look like:
$command = ".`"Command1 $($item.name) | Command2 -Switch $variable1 -Switch2 $variable2`""

Combining and modifying objects from an array in powershell to create a new array

I have a csv with columns that contain a user's first name last name. I have firgured out how to import the csv data from each column using:
$firstname = Import-csv .\data.csv | select-object "Employee First Name"
$lastname = Import-csv .\data.csv | select-object "Employee Last Name"
I have also figured out that:
$username=$firstname.substring(0,1)+$lastname
works when using system.string objects. The problem is I need to loop through all of names to create a new column with the username and I get the error
System.Object[]] doesn't contain a method named 'substring'
I thought I could convert to string using $firstname.ToString() but I seem to have botched that. Any help is geatly appreciated as I am still trying to learn powershell.
The error is self expressive $firstname is not a string. $firstname is an array of string.
Try :
$username = ($firstname[0]).substring(0,1)+$lastname[0]
you'd better use :
$employees = Import-csv .\data.csv
foreach ($employee in $employees)
{
$username=($employee.'Employee First Name').substring(0,1)+($employee.'Employee Last Name')
}
Looks like you are trying to make a user name. So i'm not sure why you want to import one column at a time?
But this might work for you.
$names = Import-Csv .\test.csv
$userNames = New-Object 'System.Collections.Generic.List[String]'
foreach ($name in $names)
{
$userNames.Add($($name.firstName.Substring(0,1) + $name.lastName))
}

Resources