There is no purpose in this example.
SyncRoot property is a way to treat in a safe manner ( generally with a lock in .net) arrays handled by more that one thread. see here and here
Extract string from json
A way to see if two variables hold references to the same array
Why is my script returning random properties?
Because you're outputting a hashtable which has those properties by default, use a PSCustomObject instead. Also refactored your script to avoid using an arraylist:
$rooms = $null
$mb = $null
$place = $null
$rooms = Get-Mailbox -RecipientTypeDetails roommailbox -ResultSize unlimited
$data = foreach ($mb in $rooms) {
$place = Get-Place -Identity $mb.WindowsEmailAddress
Write-Output ([PSCustomObject]@{
Displayname = $mb.displayname
Emailaddress = $mb.primarysmtpaddress
Capacity = $mb.resourcecapacity
equipment = $mb.ResourceCustom
label = $place.label
Floorlabel = $place.floorlabel
phone = $place.phone
hidden = $mb.HiddenFromAddressListsEnabled
})
}
$data | Export-Csv C:\scripts\confroom.csv -NoTypeInformation
Example:
$x = @{}
$x | select *
#Output:
#IsReadOnly : False
#IsFixedSize : False
#IsSynchronized : False
#Keys : {}
#Values : {}
#SyncRoot : System.Object
#Count : 0
More on reddit.com Going Crazy... Am I doing something wrong?
Instead of adding props to the array just write-output it (or just use $props on a line without anything else and it will default to Write-output). That will let the pipeline handle it and pass each one to the next thing, in this case export-csv
You're extra write-output strings should be write-verbose, write-warning or write-information (if psv5+).
More on reddit.comVideos
I have a script to collect all rooms in exchange but the output is showing me the following:
Capacity Count IsFixedSize IsReadOnly IsSynchronized SyncRoot
8 8 FALSE FALSE FALSE System.Object
What am I missing?
$rooms = $null
$mb = $null
$place = $null
$data = [System.Collections.ArrayList]@()
$rooms = Get-Mailbox -RecipientTypeDetails roommailbox -ResultSize unlimited
foreach ($mb in $rooms) {
$place = Get-Place -Identity $mb.WindowsEmailAddress
$result = @{
Displayname = $mb.displayname
Emailaddress = $mb.primarysmtpaddress
Capacity = $mb.resourcecapacity
equipment = $mb.ResourceCustom
label = $place.label
Floorlabel = $place.floorlabel
phone = $place.phone
hidden = $mb.HiddenFromAddressListsEnabled
}
[void]$data.Add($result)
}
$data | Export-Csv C:\scripts\confroom.csv -NoTypeInformation
SOLVED: I promise not to pollute my output streams ever again. Removing all the Write-Output statements fixed the issue. Big thanks to u/-j-e-s- and u/SeeminglyScience. After watching a lot of Don Jones videos, I've had it drilled into my brain that Write-Host is bad and Write-Output is good. For those that don't know, Don Jones says, "Every time you use Write-Host, a puppy dies." Maybe this was one instance where Write-Host would of been good.
Ok so I have a very weird issue and it's been driving me insane. I've been getting more and more into Powershell and I am trying to build things into tools, instead of one long script. So I made an advanced function that gets some data for expiring AD user passwords that I plan to use as part of a larger script, but serves a purpose on it's own as well.
So I have everything output as an object for maximum versatility. All this displays correctly in the console. If I specify some usernames and export-csv it works great. If I pipe some users from a text file it works great.
This works...
Get-ExpiringPasswords -Usernames "Johnny Test" | Export-Csv -Path "C:\output.csv" -Notypeinformation
So does this...
Get-Content "C:\users.txt" | Get-ExpiringPasswords
With these results in the console...
Name : Johnny Test Mail : JTest@email.com PasswordLastSet : 7/19/2017 11:53:37 AM PasswordExpirationDate : 10/17/2017 11:53:37 AM PasswordDaysToExpired : 89
Here's where it gets strange. If I do the following code, it exports strings length data and doesn't produce the expected results in the CSV.
Get-Content "C:\users.txt" | Get-ExpiringPasswords | Export-CSV -Path C:\output.csv -Notypeinformation
When I pipe the results to Get-Member it does confirm it is outputting PSCustomObjects with the correct properties.
TypeName: System.Management.Automation.PSCustomObject Name MemberType Definition ---- ---------- ---------- Equals Method bool Equals(System.Object obj) GetHashCode Method int GetHashCode() GetType Method type GetType() ToString Method string ToString() Mail NoteProperty string Mail=JTest@email.com Name NoteProperty string Name=Johnny Test PasswordDaysToExpired NoteProperty System.Int32 PasswordDaysToExpired=89 PasswordExpirationDate NoteProperty System.DateTime PasswordExpirationDate=10/17/2017 11:53:37 AM PasswordLastSet NoteProperty datetime PasswordLastSet=7/19/2017 11:53:37 AM
If I break the rules of toolmaking and pipe my $array of objects to Export-CSV at the end of the script, that gives me the desired results as well.
So change this...
End
{
Write-Output $array
}To this....
End
{
$array | Export-Csv -Path C:\output.csv -NoTypeInformation
}So why can't I pipe a list of users to the function, output pscustomobjects, and export the results as CSV on the pipeline?
Here is the code(It requires the active directory module)...
*Edit: Moved the script to pastebin
https://pastebin.com/jpRivbVf
*Edit2: Example of the CSV output
Count Length LongLength Rank SyncRoot IsReadOnly IsFixedSize IsSynchronized 459 459 459 1 System.Object[] FALSE TRUE FALSE
Instead of adding props to the array just write-output it (or just use $props on a line without anything else and it will default to Write-output). That will let the pipeline handle it and pass each one to the next thing, in this case export-csv
You're extra write-output strings should be write-verbose, write-warning or write-information (if psv5+).
You're mixing your output types, it doesn't know what properties to use. So it exports the array itself.
In other words, don't output the strings. Use Write-Verbose, Write-Warning, Write-Debug, or Write-Error.
Solved: I tried to pipe the data straight into my queries instead of saving the results to a variable first.
I'm trying to build a quick Sonarr and Radarr function to automate some tasks on my Plex Server. I have the function working and it's making API calls fine. I'm unable to search the data without saving the data to a local json file on my computer.
It's working this way, but I was wondering if there was a better way to do it.
-
When use the function, my data comes back like this. I assumed I'd be able to select properties from this but no luck.
-
When I pipe invoke-restmethod to get-member, the type is System.Object
-
All of the data I need is in the SyncRoot property
-
It works if I pipe this to ConvertTo-Json, then to Out-File, and then in another command load the data using Get-Content and ConvertFrom-Json from the local file I just created.
-
The script is a WIP. What it's going to do is grab a list of files that are older than 90 days, grab the file paths, and then pipe that into the Sonarr/Radarr API so it can delete them from Sonarr/Radarr and delete the files from my server.
Is there a better way to be able to search the data it pulls from the API?
Have you looked at Robocopy (Robust File Copy)? It can be used with PS and provides what your looking for i.e. it is designed for reliable copying or mirroring of folders (changes/adds/removes) just select the options as required.
Robocopy sourceFolder destinationFolder /MIR /FFT /Z /XA:H /W:5
The /MIR option mirrors the source directory and the destination directory. It will delete files at the destination if they were deleted at the source.
Robocopy
I think you should try the following, it works for me change the syncMode base on your requirement. 1 is for one-way sync source to target, 2 is dual-way sync
$source="The source folder"
$target="The target folder"
touch $source'initial.initial'
touch $target'initial.initial'
$sourceFiles=Get-ChildItem -Path $source -Recurse
$targetFiles=Get-ChildItem -Path $target -Recurse
$syncMode=2
try{
$diff=Compare-Object -ReferenceObject $sourceFiles -DifferenceObject $targetFiles
foreach($f in $diff) {
if($f.SideIndicator -eq "<=") {
$fullSourceObject=$f.InputObject.FullName
$fullTargetObject=$f.InputObject.FullName.Replace($source, $target)
Write-Host "Attemp to copy the following: " $fullSourceObject
Copy-Item -Path $fullSourceObject -Destination $fullTargetObject
}
if($f.SideIndicator -eq "=>" -and $syncMode -eq 2) {
$fullSourceObject=$f.InputObject.FullName
$fullTargetObject=$f.InputObject.FullName.Replace($target,$source)
Write-Host "Attemp to copy the following: " $fullSourceObject
Copy-Item -Path $fullSourceObject -Destination $fullTargetObject
}
}
}
catch {
Write-Error -Message "something bad happened!" -ErrorAction Stop
}
rm $source'initial.initial'
rm $target'initial.initial'
Try this out instead:
# Set user input to $Input
$UserInput = Read-Host -Prompt "Which sync type? (Delta|Full)"
# Use a switch statement instead of multiple If statements:
Switch ($UserInput ) {
'Delta' {
Write-Host -ForegroundColor Cyan "Starting a Delta Sync"
Start-ADSyncSyncCycle -PolicyType Delta
Write-Host -ForegroundColor Green "Delta Sync has finished"
}
'Full' {
Write-Host -ForegroundColor Cyan "Starting a Full Sync"
Start-ADSyncSyncCycle -PolicyType Initial
Write-Host -ForegroundColor Green "Full Sync has finished"
}
# Make an error if the user didn't enter a correct value
default {
throw 'Input not recognized, please specify either Full or Delta. Exiting...'
}
}
Code in variable definitions (like $a = Do-TheThing) runs! The variable only gets the output. Be very careful with things like this:
$DS = Start-ADSyncSyncCycle -PolicyType Delta
$FS = Start-ADSyncSyncCycle -PolicyType Initial
That code was running both sync types like you saw instead of setting the command to be run later.
Cpt.Whale's answer already pointed out that both cmdlets will run:
$DS = Start-ADSyncSyncCycle -PolicyType Delta
$FS = Start-ADSyncSyncCycle -PolicyType Initial
If you want to save a command in a variable for later execution you do so like this:
$DS = { Start-ADSyncSyncCycle -PolicyType Delta } # Store the ScriptBlock
& $DS # Execute the ScriptBlock
As for how you can approach your script, you can create a Menu like below that only accepts specific user input:
$menu = {
Clear-Host
@'
================ AD Sync ================
[1] - For Full Sync
[2] - For Delta Sync
[Q] - Quit
'@
}
$goTo = {
while(-not $goToMenu -or $goToMenu -notmatch '^y$|^n$')
{
''
$goToMenu = Read-Host 'Go back to Menu? [Y/N]'
switch($goToMenu)
{
'y' { rv selection -Scope script }
'n' { break }
}
}
}
$userInput = {Read-Host 'Selection'}
while(-not $selection -or $selection -notmatch '^1$|^2$|^q$')
{
& $menu
$selection = & $userInput
switch($selection)
{
1 {
Write-Host -ForegroundColor Cyan "Starting a Full Sync"
Start-ADSyncSyncCycle -PolicyType Initial
Write-Host -ForegroundColor Green "Full Sync has finished"
& $goTo
}
2 {
Write-Host -ForegroundColor Cyan "Starting a Delta Sync"
Start-ADSyncSyncCycle -PolicyType Delta
Write-Host -ForegroundColor Green "Delta Sync has finished"
& $goTo
}
Q { break }
}
}
Normally, the Azure AD sync will run every 15-30 minutes and should be allowed to run its course as per schedule.
However, there may be times when you want to force a synch and this PowerShell logic will do that. It assumes you have a server that you have access to that runs the Azure AD Sync service. The script simply triggers a delta (changes only) sync.
# Office 365 - Force Azure AD Connect Resynchronization (Delta Changes Only)
Clear-Host
# Connect to server hosting the Azure AD Connect synch service to carry out the command
# Note: We need to use the New-PSSessionOption command to adjust the connection default to NOT use a proxy server.
$SyncServer = "YOURSYNCSERVER.fqdn"
$SyncServerSession = New-PSSession -ComputerName $SyncServer -SessionOption (New-PSSessionOption -ProxyAccessType NoProxyServer)
Invoke-Command -Session $SyncServerSession -ScriptBlock {Start-ADSyncSyncCycle -PolicyType Delta}
# Get our Office 365 PSSession and close it
Get-PSSession | Where-Object {$PSItem.ComputerName -eq $SyncServer} | Remove-PSSessionYou can also deploy a scheduled task to the sync server that triggers the delta sync based on specific EventIDs (i.e. new user created, password/membership change, etc).
We just run the command Start-ADSyncSyncCycle -PolicyType Delta from a powershell session on the DC. is this not normal?