Multiple variables in Foreach loop [PowerShell] - powershell

Is it possible to pull two variables into a Foreach loop?
The following is coded for the PowerShell ASP. The syntax is incorrect on my Foreach loop, but you should be able to decipher the logic I'm attempting to make.
$list = Get-QADUser $userid -includeAllProperties | Select-Object -expandproperty name
$userList = Get-QADUser $userid -includeAllProperties | Select-Object -expandproperty LogonName
if ($list.Count -ge 2)
{
Write-host "Please select the appropriate user.<br>"
Foreach ($a in $list & $b in $userList)
{
Write-host "<a href=default.ps1x?UserID=$b&domain=$domain>$b - $a</a><br>"}
}
}

Christian's answer is what you should do in your situation. There is no need to get the two lists. Remember one thing in PowerShell - operate with the objects till the last step. Don't try to get their properties, etc. until the point where you actually use them.
But, for the general case, when you do have two lists and want to have a Foreach over the two:
You can either do what the Foreach does yourself:
$a = 1, 2, 3
$b = "one", "two", "three"
$ae = $a.getenumerator()
$be = $b.getenumerator()
while ($ae.MoveNext() -and $be.MoveNext()) {
Write-Host $ae.current $be.current
}
Or use a normal for loop with $a.length, etc.

Try like the following. You don't need two variables at all:
$list = Get-QADUser $userid -includeAllProperties
if ($list.Count -ge 2)
{
Write-Host "Please select the appropriate user.<br>"
Foreach ($a in $list)
{
Write-Host "<a href=default.ps1x?UserID=$a.LogonName&domain=$domain>$a.logonname - $a.name</a><br>"
}
}

Related

Powershell script with Contains operator not working

I am completely stuck! Hoping someone can help point out what I'm doing wrong. PS script below.
For testing I have 2 users in "newusers.csv" and the same 2 users in "currentmembers.csv". These are teh same file just renamed. Results of script are that the 2 users in "newusers.csv" are NOT members of "currentmembers.csv". This is obviously wrong but I can't figure out what is wrong with the script.
$list = Import-Csv newusers.csv
$members = Import-Csv currentmembers.csv
foreach ($UPN in $list) {
If ($members-Contains $UPN.upn) {
Write-Host $UPN.upn "is member"
} Else {
Write-Host $UPN.upn "is not a member"
}}
Results
user 1 is not a member
user 2 is not a member
Your two Import-CSV calls are creating an array of objects, each containing two objects with 1 value (UPN). Your use of -contains will not work here as, you are not comparing directly to a string, more info here.
You have two options.
#1 you can use -in
foreach ($UPN in $list) {
If ($UPN.upn -in $members.upn) {
Write-Host $UPN "is member"
}
Else {
Write-Host $UPN "is not a member"
}
}
#2 You can use -match
foreach ($UPN in $list) {
If ($members -match $UPN.upn) {
Write-Host $UPN.upn "is member"
}
Else {
Write-Host $UPN.upn "is not a member"
}
}
Completely agree with Owain's answer, but I got halfway through mine so I thought I'd finish it. You can try playing with sample data directly in PowerShell as follows, using ConvertFrom-Csv, which saves you editing files and then hopping back into PowerShell:
$list = #'
upn
user 1
user 2
user 7
user 99
'# | ConvertFrom-Csv
$members = #'
upn
user 2
user 1
user 4
'# | ConvertFrom-Csv
foreach ($UPN in $list) {
If ($UPN.upn -in $members.upn) {
Write-Host $UPN.upn "is member"
} Else {
Write-Host $UPN.upn "is not a member"
}}
As Owain said, you need to alter the comparison part to compare a string to an array.
Your $member should be an array of strings, not array of objects. Same thing as you are comparing $upn.UPN - the $upn is a object and you're accessing UPN member of it.
Hope that I explained this clearly
$members = Import-Csv currentmembers.csv | select -expandproperty ColumnName

Powershell find value in arraylist

I hope you can help me out. I work with two ArrayLists:
array1 is filled with log.csv (contains header and logon-data, values of column 'pc' are unique). It's defined as ArrayList because I want to add entries.
#pc,name,date,city
#pc-x,name-1,2017-01-01,berlin
#pc-y,name-1,2017-01-02,berlin
#pc-z,name-2,2017-01-02,munich
[System.Collections.ArrayList]$array1 = Import-Csv log.csv
array2 is filled during runtime
$array2=[System.Collections.ArrayList]#()
... ForEach-Object {
$array2.Add([PSCustomObject]#{ pc=$pcname
name=$loginname
date=$logindate }) >$null }
What I want to do:
Update array1.date=array2.date where array1.pc=array2.pc
If no entry found in array1 I want to add it:
$array1.Add([PSCustomObject]#{ pc=$pcname
name=$loginname
date=$logindate
city='[unknown]' }) >$null
Finally array1 is exported again:
$array1 | Export-Csv log.csv -Encoding UTF8 -NoTypeInformation
So the question is: how can I find the entry in array1 and update it? Trying hard for days now ...
try Something like this:
$array1=import-csv "C:\temp\log.csv"
$array2=import-csv "C:\temp\log2.csv"
#modify founded and output not founded
$toadd=$array2 | %{
$current=$_
$founded=$array1 | where pc -eq $current.pc | %{$_.date=$current.date;$_}
if ($founded -eq $null)
{
$current.city='UNKNOW'
$current
}
}
#output of $array1 modified and elements to add
$array1, $toadd
Here is a sample I created that might help. Note: I use List types instead of ArrayList ones. Also, it assumes only one possible matching PC name in the data to be updated. You'll have to alter it to update the file since it merely updates the first List variable. Let me know how it goes.
[PSCustomObject]
{
[string] $pc,
[string] $name,
[string] $date,
[string] $city
}
[System.Collections.Generic.List[PSCustomObject]] $list1 = Import-Csv "C:\SOSamples\log.csv";
[System.Collections.Generic.List[PSCustomObject]] $list2 = Import-Csv "C:\SOSamples\log2.csv";
[PSCustomObject] $record = $null;
[PSCustomObject] $match = $null;
foreach($record in $list2)
{
# NOTE: This only retrieves the FIRST MATCHING item using a CASE-INSENSITIVE comparison
$match = $list1 | Where { $_.pc.ToLower() -eq $record.pc.ToLower() } | Select -First 1;
if($match -eq $null)
{
Write-Host "Not Found!";
$list1.Add($record);
}
else
{
Write-Host "Found!";
$match.date = $record.date;
}
}
Write-Host "--------------------------------------------------------------------"
foreach($record in $list1)
{
Write-Host $record
}

Select option from Array

I am working on a side project and to make it easier for managment since almost all of out server names are 15 charactors long I started to look for an RDP managment option but none that I liked; so I started to write one and I am down to only one issue, what do I do to manage if the user types not enough for a search so two servers will match the Query. I think I will have to put it in an array and then let them select the server they meant. Here is what I have so far
function Connect-RDP
{
param (
[Parameter(Mandatory = $true)]
$ComputerName,
[System.Management.Automation.Credential()]
$Credential
)
# take each computername and process it individually
$ComputerName | ForEach-Object{
Try
{
$Computer = $_
$ConnectionDNS = Get-ADComputer -server "DomainController:1234" -ldapfilter "(name=$computer)" -ErrorAction Stop | Select-Object -ExpandProperty DNSHostName
$ConnectionSearchDNS = Get-ADComputer -server "DomainController:1234" -ldapfilter "(name=*$computer*)" | Select -Exp DNSHostName
Write-host $ConnectionDNS
Write-host $ConnectionSearchDNS
if ($ConnectionDNS){
#mstsc.exe /v ($ConnectionDNS) /f
}Else{
#mstsc.exe /v ($ConnectionSearchDNS) /f
}
}
catch
{
Write-Host "Could not locate computer '$Computer' in AD." -ForegroundColor Red
}
}
}
Basically I am looking for a way to manage if a user types server1
that it will ask does he want to connect to Server10 or Server11 since both of them match the filter.
Another option for presenting choices to the user is Out-GridView, with the -OutPutMode switch.
Borrowing from Matt's example:
$selection = Get-ChildItem C:\temp -Directory
If($selection.Count -gt 1){
$IDX = 0
$(foreach ($item in $selection){
$item | select #{l='IDX';e={$IDX}},Name
$IDX++}) |
Out-GridView -Title 'Select one or more folders to use' -OutputMode Multiple |
foreach { $selection[$_.IDX] }
}
else {$Selection}
This example allows for selection of multiple folders, but can you can limit them to a single folder by simply switching -OutPutMode to Single
I'm sure what mjolinor has it great. I just wanted to show another approach using PromptForChoice. In the following example we take the results from Get-ChildItem and if there is more than one we build a collection of choices. The user would select one and then that object would be passed to the next step.
$selection = Get-ChildItem C:\temp -Directory
If($selection.Count -gt 1){
$title = "Folder Selection"
$message = "Which folder would you like to use?"
# Build the choices menu
$choices = #()
For($index = 0; $index -lt $selection.Count; $index++){
$choices += New-Object System.Management.Automation.Host.ChoiceDescription ($selection[$index]).Name, ($selection[$index]).FullName
}
$options = [System.Management.Automation.Host.ChoiceDescription[]]$choices
$result = $host.ui.PromptForChoice($title, $message, $options, 0)
$selection = $selection[$result]
}
$selection
-Directory requires PowerShell v3 but you are using 4 so you would be good.
In ISE it would look like this:
In standard console you would see something like this
As of now you would have to type the whole folder name to select the choice in the prompt. It is hard to get a unique value across multiple choices for the shortcut also called the accelerator key. Think of it as a way to be sure they make the correct choice!

Compare two csv files and find discrepancies

I am new in powershell world , I got some project on powershell for inventory reconciling .
I am not sure how to proceed on this , I tried some basic steps and I am able to export
users/group/group membership.
Following are the requirements :
Now What I have achieved - Thanks to Govnah
AD query:
Get-QADUser -searchRoot $OuDomain -SizeLimit 0 |
Select-Object dn, sAMAccountName, #{Name="Groups";Expression={(Get-QADMemberOf $_ | Select-Object -expandProperty Name) -join ";"}} |
Sort-Object SamAccountName |
export-csv $FilePath
I have now two csv files likes AD_users.csv and Oracle_users.csv
I want to compare both files and redirect the difference like
AD users does not exist in Oracle
Oracle User does not exist in AD
Sample data
AD_users.csv
u388848993
K847447388
u994888484
Oracle_users.csv
k095848889
u388848993
I can query oracle database , AD query is also fine the only concern is that I am not able to compare the output.
I did it something like this in a script I wrote:
[System.Collections.ArrayList]$adlist = Get-Content c:\users\sverker\desktop\ad.csv |Sort-Object
[System.Collections.ArrayList]$oraclelist = Get-Content c:\users\sverker\desktop\oracle.csv |Sort-Object
$Matching_numbers = #()
ForEach ($number in $adlist)
{
if ($oraclelist.Contains($number))
{
$Matching_numbers += $number
}
}
ForEach ($number in $Matching_numbers)
{
$adlist.Remove($number)
$oraclelist.Remove($number)
}
now $Matching_numbers now contains the matching numbers
and $adlist contains only numbers from AD
and $oraclelist only numbers from Oracle
you can then loop through the list and display values:
Write-Host "Matches:"
ForEach ($value in $Matching_numbers)
{
$Message += $value + [Environment]::NewLine
}
Write-Host $Message
Write-Host "AD only:"
ForEach ($value in $adlist)
{
$MessageAd += $value + [Environment]::NewLine
}
Write-Host $MessageAd
Write-Host "Oracle only:"
ForEach ($value in $oraclelist)
{
$MessageOracle += $value + [Environment]::NewLine
}
Write-Host $MessageOracle
or simply by writing
$Matching_numbers
will output the list to console
You can output the $Message variables to a file or so..
No doubt, there is a nicer way to do it, but this worked for me for a certain type of file.

filtering and though a sharepoint list items with powershell

I have tried below but not getting any result back
Not sure if i'm doing this well.
Can i filter in the foreach or in my if statement
Thanks in advance
[DateTime] $CreatedDate = $item["Created"]
$convertedCreatedDate = $CreatedDate.ToString("yyyy-MM-dd")
$today = (Get-Date).AddDays(-1).ToString("yyyy-MM-dd")
foreach ($item in $list.items | where {$convertedCreatedDate -eq $today}) {
if ($list.items | where {$convertedCreatedDate -eq $today})
{
Write-Host $item["Created"]
}
Write-Host $item["Created"]
}
You can use a complex expression in your foreach as you're doing above. I would wrap it in a #() to make the code a bit more readable and to ensure the result is an array (either length 0, 1 or n) e.g.:
foreach ($item in #($list.items | where {$convertedCreatedDate -eq $today})) {
You can also simplify you're date testing by using the Date property on a DateTime e.g.:
$convertedCreatedDate = ([DateTime]$item["Created"]).Date
$today = (Get-Date).Date
You can also put a complex expression within an if statement condition but PowerShell will only evaluate whether the statement is $true or $false. PowerShell does lots of coercion to try to take something like:
$list.items | where {$convertedCreatedDate -eq $today}
And convert that to a boolean. Essentially if the pipeline evaluates to a non-empty result, the result is $true otherwise it is $false. This is probably not what you intended.
Try this:
$today=(Get-Date).AddDays(-1).ToString("yyyy-MM-dd")
foreach ($item in $list.items) {
[DateTime]$CreatedDate=$item["Created"]
$convertedCreatedDate=$CreatedDate.ToString("yyyy-MM-dd")
if ($convertedCreatedDate -eq $today) {
Write-Host $item["Created"]
}
}