I run an all users command for calendar delegation. I then report. The issue is how do I include someone that has no calendar delegation ? (Code Below)
In this line If ($null -ne $DelegateCal) I make sure someone has calendar delegation then build the object.
If I don't use += I am not sure how to build the object when I add a Else for the $null
<#All Google Calendar delegate report#>
# filename function
Import-Module C:\tasks\Modules\MRNAP\MRNAP.psm1
$AllGoogleUsers = gam print users fields suspended | ConvertFrom-Csv | Where-Object { $_.suspended -eq $False } | Select-Object -ExpandProperty PrimaryEmail
ForEach ($UserEmail in $AllGoogleUsers) {
$DelegateCal = gam calendar $UserEmail print acls | convertfrom-csv | Where-Object { $_.'scope.type' -eq 'user' -and $_.'Scope.value' -ne $UserEmail } -ErrorAction SilentlyContinue
If ($null -ne $DelegateCal) {
$CalendarDelegateList = foreach ($line in $DelegateCal) {
[PSCustomObject]#{
Owner = $line.calendarId
Type = 'Calendar'
Delegate = $line.'scope.value'
Role = $line.role
}
}
}
}
$CalendarDelegateList = $CalendarDelegateList | Sort-Object -Property Owner
$filename = MRNAP -ReportName WhoIsCalendarDelegated -Move
$CalendarDelegateLis | Export-Csv $filename -NoTypeInformation | Format-Table text-align=left -AutoSize
This is how I would do it with +=
$AllGoogleUsers = gam print users fields suspended | ConvertFrom-Csv | Where-Object { $_.suspended -eq $False } | Select-Object -ExpandProperty PrimaryEmail
ForEach ($UserEmail in $AllGoogleUsers) {
$DelegateCal = gam calendar $UserEmail print acls | convertfrom-csv | Where-Object { $_.'scope.type' -eq 'user' -and $_.'Scope.value' -ne $UserEmail } -ErrorAction SilentlyContinue
If ($null -ne $DelegateCal) {
foreach ($line in $DelegateCal) {
$CalendarDelegateList += [PSCustomObject]#{
Owner = $UserEmail
Type = 'Calendar'
Delegate = $line.'scope.value'
Role = $line.role
}
}
}
Else {
$CalendarDelegateList += [PSCustomObject]#{
Owner = $UserEmail
Type = 'Calendar'
Delegate = 'None'
Role = 'None'
}
}
}
It is always preferable to let PowerShell collect statement output in an array for you rather than building a list of outputs manually - both for concision and performance; see this answer for more information.
This even works with nested foreach loops, as in your case.
Applied to your scenario (abridged):
[array] $CalendarDelegateList =
foreach ($UserEmail in $AllGoogleUsers) {
$DelegateCal = gam calendar $UserEmail print acls | ConvertFrom-Csv | Where-Object { $_.'scope.type' -eq 'user' -and $_.'Scope.value' -ne $UserEmail } -ErrorAction SilentlyContinue
If ($null -ne $DelegateCal) {
foreach ($line in $DelegateCal) {
# Construct *and ouput* a [pscustomobject] instance.
[PSCustomObject]#{
Owner = $UserEmail
# ...
}
}
}
Else {
[PSCustomObject]#{
Owner = $UserEmail
# ...
}
}
}
All [pscustomobject] instances (implicitly) output from inside the foreach loop (whether directly or from the nested one) are automatically collected in variable $CalendarDelegateList.
Note:
With two or more output objects from the loop, the $CalendarDelegateList variable receives a regular PowerShell array (of type [object[]]).
The [array] type constraint (short for: [object[]]) additionally ensures that the result is an array even if the loop outputs only one object.
I am trying to print the variable whenever the variable $PrinterStatus is returning any data but the correct data is not coming with If else logic.
$CurrentTime = Get-Date
$PrinterStatus=
Get-Printer -ComputerName "TGHYT-6578UT" | Foreach-Object {
$Printer = $_
$Printer | Get-Printjob |
Where-Object {$_.jobstatus -ne "Normal" -and $_.SubmittedTime -le $CurrentTime.AddHours(-1) } |
Select-Object #{name="Printer Name";expression={$_.printerName}},
#{name="Submitted Time";expression={$_.SubmittedTime}},
jobstatus, #{name="Port";expression={$Printer.PortName}},
#{name="Document Name";expression={$_.documentname}},
#{n='Difference in Hours';e={[math]::Truncate(($CurrentTime - $_.SubmittedTime).TotalHours)}} |
Sort-Object -Property jobstatus -Descending
}
if([string]::IsNullOrEmpty($PrinterStatus))
{
Write-Output "Printers NOT Present"
$output = $PrinterStatus > "C:\Output.txt" #Shoud give blank txt file
}
else {
Write-Output "printers Present"
$output = $PrinterStatus > "C:\Output.txt"
}
As your $PrinterStatus will be an array of your custom print job objects, you can check the length of that array.
$CurrentTime = Get-Date
$PrinterStatus = #()
$PrinterStatus = Get-Printer -ComputerName "TGHYT-6578UT" | Foreach-Object {
Get-Printjob $_ |
Where-Object {$_.jobstatus -ne "Normal" -and $_.SubmittedTime -le $CurrentTime.AddHours(-1) } |
Select-Object #{name="Printer Name";expression={$_.printerName}}, #{name="Submitted Time";expression={$_.SubmittedTime}}, #{name="jobstatus";expression={$_.jobstatus}}, #{name="Port";expression={$Printer.PortName}}, #{name="Document Name";expression={$_.documentname}}, #{n='Difference in Hours';e={[math]::Truncate(($CurrentTime - $_.SubmittedTime).TotalHours)}} |
Sort-Object -Property jobstatus -Descending
}
if ($PrinterStatus.Count -eq 0) {
Write-Output "Printers NOT Present"
} else {
Write-Output "Printers Present"
}
$PrinterStatus > "C:\Output.txt"
I also cleaned up your code a little and fixed the insertion of jobstatus in your custom object.
I am trying to update an hash table entry (Content, which is an array) where I find values - but when I try to set the value for $_ it applies to all entries in the entire hash ($hash).
$ContentArray = #($null, $null)
$Comparison | Add-Member -MemberType NoteProperty -Name "Content" -value $ContentArray
If($GetAdvancedData -eq "true"){
$Hash| ForEach-Object{
If ($_.VarianceType -ne "Missing")
{
$_.Item
$id = $_.id[0]
$elem = $_.elementName
Write-debug "Checking $elem"
Write-debug "Checking for version $id"
try{
$content = Get-VersionContent -FilteredVersionID $id
Write-debug "Content found"
Write-debug "$content"
# Various tests to try and set the value:
#ForEach ($Key in $Hash.GetEnumerator() | Where-Object {$_.id -eq $id}){$Key.Content[0] = $content}
#$Hash.Content[0] = "$content" | Where-Object {$Hash.id[0] -eq $id}
#$Hash.DiffType = "Content"| Where-Object {$_.id[0] -eq $id}
# $_.content.SetValue("$content","0")
$_.Content[0] = $content
# Reset $content to Null
$content = $null
}
catch
{
Write-debug "No content found"
}
}
}
I have tried setting it via a where clause based on another key value, using SetValue, and simply doing an = statement, but in each case it sets the entire hash tables content to $content - I feel as if I must be missing something obvious, but I can't see why (if I use the PowerShell ISE and debug $_ returns only the single record from the ForEach loop)
A silly mistake-
$Hash.GetEnumerator() | Where-Object {$_.id[0] -eq $id} | foreach{$_.Content = $content,$null}
did the trick for me!
I have a CSV file that contains subnet information that I will use to populate a CSV file that has server information in it. I'm starting by importing the subnet information and when processing it, I'm trying to add multiple members to the initial hashtable, but it is not behaving as expected.
The following code processes the first item as expected, creating a new column with the correct information. The code indicates that it processes at least through two more sections, but the members are not added. How do I change the code to allow the creation of multiple members to a single array? The goal is to have each subnet's gateway field (column) be unique to that subnet.
The purpose of the five variables (variableA-E) is to mimic what is occuring in the real code. The real code runs comparisons from the hashtable, but that is not neccessary. I'm willing to change that portion if needed.
CSV file contents:
NetworkName,Subnet,VLANID,Gateway,VLAN
Servers,"192.168.1.0/24","2041","192.168.1.1","ServerVLAN-2041"
Workstations,"192.168.2.0/24","1001","192.168.2.1","WorkstationVLAN-1001"
DMZ,"172.16.0.0/28","340","172.16.0.1","DMZVLAN-340"
Servers,"192.168.3.0/24","2043","192.168.3.1","ServerVLAN-2043"
Workstations,"192.168.4.0/24","1004","192.168.4.1","WorkstationVLAN-1004"
DMZ,,,,
CODE:
$csvfile = "C:\temp\testfile.csv"
$hashArray = Import-CSV $csvfile
$variableA = "192.168.1.0"
$variableB = "192.168.2.0"
$variableC = "192.168.3.0"
$variableD = "172.16.0.1"
$variableE = "192.168.5.0"
$hashArray | % {
if ($_.subnet) { $variable = ($_.subnet).split("/")[0] }
Else { $variable = $null }
if ($variable -eq $variableA -and $variable -ne $null)
{
$_ | add-member "ServerGW1" -NotePropertyValue $_.gateway
Write-Host "Added Server gateway 1: "$_.gateway -ForegroundColor Yellow
}
if ($variable -eq $variableC -and $variable -ne $null)
{
$_ | add-member "ServerGW2" -NotePropertyValue $_.gateway
Write-Host "Added Server gateway 2: "$_.gateway -ForegroundColor Yellow
}
if ($variable -eq $variableB -and $variable -ne $null)
{
$_ | add-member "WorkstationGW1" -NotePropertyValue $_.gateway
Write-Host "Added Workstation gateway 1: "$_.gateway -ForegroundColor Yellow
}
if ($variable -eq $variableD -and $variable -ne $null)
{
$_ | add-member "DMZGW1" -NotePropertyValue $_.gateway
Write-Host "Added DMZ gateway 1: "$_.gateway -ForegroundColor Yellow
}
if ($variable -eq $variableE -and $variable -ne $null)
{
$_ | add-member "WorkstationGW2" -NotePropertyValue $_.gateway
Write-Host "Added Workstation gateway 2: "$_.gateway -ForegroundColor Yellow
}
}
$hashArray | Out-GridView
Out-GridView OUTPUT:
Console OUTPUT:
Expected output:
Out-GridView uses properties from the first object to render columns. All new columns (except ServerGW1) are missing because are not initialized in the first object in your $hashArray. You can initialize with $null value all properties for all rows or provide a list of properties to select before you output your result to Out-GridView
$hashArray | Select-Object NetworkName,Subnet,VLANID,Gateway,VLAN, ServerGW1, ServerGW2,WorkstationGW1,WorkstationGW2,DMZGW1 | Out-GridView
Init all properties:
$hashArray | % {
$variable =if ($_.subnet) { ($_.subnet).split("/")[0] }Else { $null }
$_ | add-member "ServerGW1" -NotePropertyValue $(if ($variable -eq $variableA){ $_.gateway}Else { $null })
$_ | add-member "ServerGW2" -NotePropertyValue $(if ($variable -eq $variableC){ $_.gateway}Else { $null })
$_ | add-member "WorkstationGW1" -NotePropertyValue $(if($variable -eq $variableB){ $_.gateway}Else { $null })
$_ | add-member "DMZGW1" -NotePropertyValue $(if ($variable -eq $variableD ){ $_.gateway}Else { $null })
$_ | add-member "WorkstationGW2" -NotePropertyValue $(if ($variable -eq $variableE){ $_.gateway}Else { $null })
}
$hashArray | Out-GridView
To complement cezarypiatek's helpful answer, which provides the crucial pointer:
All formatting cmdlets, including Out-GridView decide what properties (columns) to show based on the first input object, so to guarantee that the all columns of interest are shown, you must ensure that (at least) the first input object contains all properties of interest.
With that in mind, here's a streamlined version of your approach that does that:
$csvfile = "C:\temp\testfile.csv"
$networks = Import-CSV $csvfile
# Define the subnets and their property names as an ordered hashtable.
$subnets = [ordered] #{
'192.168.1.0' = 'ServerGW1'
'192.168.2.0' = 'ServerGW2'
'192.168.3.0' = 'WorkstationGW1'
'172.16.0.0' = 'DMZGW1'
'192.168.4.0' = 'WorkstationGW2'
}
# Add all properties of interest to the input objects, to ensure
# that Out-GridView (or other formatting cmdlets) show them all.
# Construct an array of property names, where '*' stands for the original properties...
$propNames = #('*') + [string[]] $subnets.Values
# ... and create augmented objects based on them.
$networks = $networks | Select-Object -property $propNames
$networks | % {
# See if the 'subnet' column has a value...
if ($subnet = if ($_.subnet) { ($_.subnet).split("/")[0] } else { $null }) {
# ... and, if so, see if a subnet name is defined for the part before '/' ...
if ($subnets.Contains($subnet)) {
# ... and, if so, fill the subnet-named property with the subnet address.
$_.($subnets.$subnet) = $subnet
}
}
}
Import-CSV doesn't return hashtables it returns custom objects ([pscustomobject] instances).
The code relies on the fact that, in the context of expressions, you can both assign to a variable and use the value of that assignment, such as in a conditional, as is the case here (if ($subnet = ...)).
try this, this example is dynamic, you have just to modify $hashvariable like you want (or load $hashvariable with file if you want)
$csvfile ="C:\temp\testfile.csv"
$hashArray = Import-CSV $csvfile
$hashvariable=[ordered]#{"192.168.1.0"="ServerGW1"; "192.168.2.0"="WorkstationGW1"; "192.168.3.0"="ServerGW2"; "172.16.0.1"="DMZGW1"; "192.168.5.0"="WorkstationGW2" }
$hashArray |
%{
$result=$_
$variable = if ($_.subnet -ne $null) {($_.subnet).split("/")[0]} else {""}
foreach ($key in $hashvariable.Keys)
{
$value=if ($variable -eq $key) {$key} else {""}
$result | add-member $hashvariable[$key] -NotePropertyValue $value
}
}
$hashArray | Out-GridView
I am trying to compare the values of two variables but the contents of those two strings are in different orders
Example:
$Var1 = "item1"
$Var1 += "item2"
$Var2 = "item2"
$Var2 = "item1"
How can I compare those two variables to see if they both are equal?
===== UPDATED WITH EXAMPLE =====
EXAMPLE: Get objects and sort them.
$Computers = (Get-Content "$PWD\Computers.txt").GetEnumerator() | Sort-Object {"$_"}
EXAMPLE: Add the results and sort them.
$Successful += $Computer
$Successful = $Successful.GetEnumerator() | Sort-Object {"$_"}
EXAMPLE SCRIPT: Used the examples above to create the following script. The example allowed me to check the results, instead of count, but by content allowing me to get more accurate comparison. Before I was using "Successful.count -eq Computers.count" which wouldn't check if a computer was inputted twice.
$Computers = (Get-Content "$PWD\Computers.txt").GetEnumerator() | Sort-Object {"$_"}
$HotFixes = Get-Content "$PWD\HotFixes.csv"
CLS
While (!$Successful -OR $Successful -ne $Computers) {
foreach ($Computer in $Computers) {
$MissingCount = 0
IF (!$Successful -NotLike "*$Computer*") {
Write-Host "$Computer`: Connecting"
If (Test-Connection -ComputerName $Computer -Count 1 -quiet) {
Write-Host "$Computer`: Connected"
[string]$Comparison = get-hotfix -ComputerName $Computer | Select -expand HotFixID
ForEach ($HotFix in $HotFixes) {
IF ($Comparison -NotLike "*$HotFix*") {
$Results += "$Computer,$HotFix"
$MissingCount++
}
}
Write-Host "$Computer`: $MissingCount Patches Needed"
$Successful += $Computer
$Successful = $Successful.GetEnumerator() | Sort-Object {"$_"}
} ELSE {
Write-Host "$Computer`: Unable to connect"
}
} ELSE {
Write-Host "$Computer already completed"
}
Write-Host "$Computer`: Complete"
Write-Host
}
}
$Results
If you want to find if the content is equal, regardless of characters position, you could break the string to its characters, sort the result and then use the Compare-Object cmdlet. No result means the variables are equal:
$v1 = $Var1.GetEnumerator() | Sort-Object {"$_"}
$v2 = $Var2.GetEnumerator() | Sort-Object {"$_"}
compare $v1 $v2