Powershell script providing false positives (Exchange Online) - powershell

I seem to be coming across issues with a check i have put in place to confirm a mailbox has ben converted from a users mailbox to shared mailbox.
Connect-ExchangeOnline -UserPrincipalName $UserCredential.UserName
Write-Host "checking mailbox has converted to a shared mailbox...."
$Mailboxcheck = Get-Mailbox "$username" | select isshared
if ($Mailboxcheck.IsShared -eq $False) {
[System.Windows.MessageBox]::Show("Mailbox for $($username) has NOT been converted to shared, please confirm.")
} else {
[System.Windows.MessageBox]::Show("Mailbox for $($username) has been converted to shared.")
}
the issue is sometimes this check will work and other times it won't (randomly). So we convert the mailbox to a shared mailbox via the above script then the check will sometimes come up as the $False message rather then the else message but when we go to manually check it after it's run the mailbox has been converted to shared.
The only things i can think of is to add the disconnect and reconnect to exchange online in case it's due to some syncing time, Possibly add a Start-sleep to the script to give it more time to sync, or its to do with the if statement and the variable $false as $mailboxcheck.isshared should only provide a value of "True" or "False"
if ($Mailboxcheck.IsShared -eq $False)
Another way i have made the script to check is with a switch statement but am unsure if that will be me any benefit and unsure if it's the right use case or should stick to the if statement
$Mailboxcheck = Get-Mailbox "$username" | select isshared
switch ($Mailboxcheck.isshared)
{
True {[System.Windows.MessageBox]::Show("Mailbox for $($username) has been converted to shared."); Break}
False {[System.Windows.MessageBox]::Show("Mailbox for $($username) has NOT been converted to shared, please confirm."); Break}
default {[System.Windows.MessageBox]::Show("Unable to check mailbox type for $($username). Please confirm it has been converted to shared.")}
}
Let me know your thoughts.

Try running this code along side the other one to see when the IsShared property actually changes:
$repeat = $true
do
{
$Mailboxcheck = Get-Mailbox "$username"
Write-Host "Date = $(get-date)" -for Green
write-host "RecipientTypeDetails = $($Mailboxcheck.RecipientTypeDetails)" -ForegroundColor Cyan
write-host "IsShared = $($Mailboxcheck.isShared)" -ForegroundColor yellow
""
sleep 10
}
while ($repeat -eq $true)

Related

Using multiple if statemens in foreach loop

In this script, I am trying to make a group (if not exist) and add users thats are not already in the group.
But the problem is he only takes the first if statement I think, because it don't seems like he is taking the next statements in the loop.
#Tweede test met if
$teams = #Here comes the csv file.
Foreach($team in $teams)
{
$Test = (Get-UnifiedGroup $team.DisplayName)
if (Get-UnifiedGroup $team.DisplayName)
{
Write-Host -ForegroundColor Green "$($team.Displayname) already exists!"
}
elseif ($Test -eq "false")
{
$Group = New-UnifiedGroup -DisplayName $team.DisplayName -Alias $team.Alias -AccessType $team.AccessType
}
foreach($Member in $Members)
{
elseif (get-UnifiedgroepLinks $team.Links)
{
write-host -ForegroundColor Green "$($team.Links) already exists!"
}
else
{
Add-UnifiedGroupLinks -Identity $team.Identity -LinkType $team.Linktype -Links $team.Links
}
}}
OK so this is my current output form #Theo last improvement
Output
OK I have something very interesting, because when I looked at the output of
$existingMembers = #((Get-UnifiedGroupLinks -Identity $Group.DistinguishedName -LinkType Members).PrimarySMTPAddress)
I will get certain blank spots. And when I ran the script for one user it was working but for another one it did not, so I looked at the user that did not worked, and he does not have a license. So I tested it further. And in my conclusion now it's working. So when an acc is unlicensed, it is not seen like he is added to that specific group. So he is also throwing the second if statement, but also the second else statement.
Does this make sense to you #Theo?
### script name: Users_Verwijderen ###
### Datum updated: 14-12-2022 ###
### Auteur: Wessel Rouw ###
### Purpose script is to add groups and users in to groups in Azure. ###
#######################################################################
$teams = import-csv #Here your CSV
foreach($team in $teams) {
$team | Format-Table
$Check = (Get-UnifiedgroupLinks -Identity $team.Identity -LinkType $team.Linktype)
$existingMembers = #((Get-UnifiedGroupLinks -Identity $Group.DistinguishedName -LinkType Members).PrimarySMTPAddress)
$Group = (Get-UnifiedGroup $team.DisplayName)
if ($Group)
{
Write-Host "$($team.Displayname) already exists!" -ForegroundColor Green
}
else
{
Write-Host "Creating group $($team.Displayname)"
$Group = New-UnifiedGroup -DisplayName $team.DisplayName -Alias $team.Alias -AccessType $team.AccessType
}
if ($existingMembers -contains $team.Links)
{
Write-Host "$($team.Links) already exists!" -ForegroundColor Green
}
else
{
Write-Host "Creating group Links $($team.Links)"
Add-UnifiedGroupLinks -Identity $team.Identity -LinkType $team.Linktype -Links $team.Links
}
}
This is now my current script working with help from #Theo (Only, users that are not licensed will get the message that they are added even if they are already in the group).
Here is my input now in my csv.
I have changed username (Links) and the domain name (After the Test101 and the users (Links).
DisplayName,Alias,AccessType,Identity,Linktype,Links
Test101,Test101,private,Test101#domain.nl,Member,Hek_Sme#Domain.nl
Test101,Test101,private,Test101#domain.nl,Member,Mek_Lei#Domain.nl
Test101,Test101,private,Test101#domain.nl,Member,Wek_Bog#Domain.nl
This is another very helpful output. again same story as above with the changed domain and usernames.
Name DisplayName GroupType PrimarySmtpA
ddress
---- ----------- --------- ------------
Test101_(**Here comes a private number)** Test101 Universal Test101#1...
PS C:\WINDOWS\system32> $existingMembers
**Here comes the domain admin**
Hek_Sme#Domain.nl
Gek_Wel#Domain.nl
Gek_Wel#Domain.nl
Dir_Bog#Domain.nl
Wek_Bog#Domain.nl
PS C:\WINDOWS\system32> $inputMembers
Mek_Lei#Domain.nl
PS C:\WINDOWS\system32>
And as you can see some fields are empty and that are exactly the two users who don't own a licence and when I then run the command to get everyone who is not presenting in the get commando of users for that group it is specified to that users again.
I only don't know why this and if my thought is right?
Oke, so thanks already for the help. Now it works better. But in the second part I want to verify that if a user already exists in azure it displays the message, but if don't it has to be added? But this is now the problem.
This is now the output of my running script
VERBOSE: Returning precomputed version info: 3.0.0
VERBOSE: POST with -1-byte payload
VERBOSE: received 2945-byte response of content type application/json;charset=utf-8
VERBOSE: Returning precomputed version info: 3.0.0
VERBOSE: POST with -1-byte payload
VERBOSE: received 2906-byte response of content type application/json;charset=utf-8
Test105 already exists!
Creating group Links #Here stand the email (Links)
VERBOSE: Returning precomputed version info: 3.0.0
VERBOSE: POST with -1-byte payload
VERBOSE: received 386-byte response of content type application/json;charset=utf-8
The problem is when a user already exist it does not goes to the line that says that it already exits but instead it skips this part i think and goed directy to the else statement.
I think I am pretty close now.
`$teams = import-csv #CSV here
{
#$team | Format-Table #This was voor debugging
$Check = (Get-UnifiedgroupLinks -Identity $team.Identity -LinkType $team.Linktype)
$Group = (Get-UnifiedGroup $team.DisplayName)
if ($Group)
{
Write-Host "$($team.Displayname) already exists!" -ForegroundColor Green
}
else
{
Write-Host "Creating group $($team.Displayname)"
$Group = New-UnifiedGroup -DisplayName $team.DisplayName -Alias $team.Alias -AccessType $team.AccessType
}
if ($Check -contains $team.Links)
{
Write-Host "$($team.Links) already exists!" -ForegroundColor Green
}
else
{
Write-Host "Creating group Links $($team.Links)"
Add-UnifiedGroupLinks -Identity $team.Identity -LinkType $team.Linktype -Links $team.Links
}
}`
These are the columns of my csv.

Loop Foreach only repeat offline computers

Not sure where I am going wrong I want to skip the computers which are coming back online but it keeps checking both online and online computers.
1st update is fine that its coming online and it should skip and only ping the offline one's a screenhot of output
g-10 Is coming online...Skipping to offline one's
192.168.0.1 Is coming online...Skipping to offline one's
Testing to see if Hero is coming online...
Hero is Offline. Pausing for 2 seconds. Remaining attempts: 1
Testing to see if zero is coming online...
zero is Offline. Pausing for 2 seconds. Remaining attempts: 1
Its fine upto here but online computers again repeated...
g-10 Is coming online...Skipping to offline one's
192.168.0.1 Is coming online...Skipping to offline one's
Testing to see if Hero is coming online...
Hero is Offline.
here is my code
$Comps = GC c:\restarted.txt
[int]$SleepTimer = "1" #minutes to attempt after
[int]$SleepSeconds = $SleepTimer * 2
[int]$Attempts = "2"
[int]$AttemptsCounter = 0
Do
{
$AttemptsCounter++
$RemainingAttempts = ([int]$Attempts - [int]$AttemptsCounter)
Foreach($comp in $comps){
$Online = Test-Connection -ComputerName $Comp -Quiet
IF($online -eq "True"){
Write-Host "$comp" -BackgroundColor Green -NoNewline
Write-Host " Is coming online...Skipping to offline one's"
}
elseIf ($Online -NE "True")
{
Write-Host "Testing to see if $Comp is coming online..."
Write-Host "$comp" -BackgroundColor Red -NoNewline
Write-Host " is Offline" -BackgroundColor Red -ForegroundColor Black -NoNewline
If ($AttemptsCounter -eq $Attempts) {
Write-Host "."
}
Else {
Write-Host ". Pausing for $SleepSeconds seconds. Remaining attempts: $RemainingAttempts"
}
}
}
#Check the number of attempts, break out if reached.
If ($AttemptsCounter -eq $Attempts) {break}
#Delay
Start-Sleep -s ($SleepTimer * 60)
}
While ($Online -NE "True")
If ($Online -NE "True") {
Write-Host "Maximum number of attempts reached"
}
Else {
Write-Host
Write-Host "Computer $Comp is " -NoNewline
Write-Host "ONLINE" -BackgroundColor Green
}
try the following code.
It basically does the following for each line in c:\restarted.txt:
tests if the computer is online (Note: I added -Count 1)
if it is online: prints some green text and break
if it is offline: print red text, wait and decrements the $RemainingAttempts variable and redo until $RemainingAttempts is 0 or the computer is online
it the $RemainingAttempts variable is 0 print out some more text
$ComputerNameArray = Get-Content -Path c:\restarted.txt
[int]$SleepTimer = "1" #minutes to attempt after
[int]$Attempts = "2"
$DefaultBackgroundColor = (Get-Host).ui.rawui.BackgroundColor
foreach($ComputerName in $ComputerNameArray) {
$AttemptsCounter = 0
$RemainingAttempts = $Attempts - $AttemptsCounter
Write-Host "Testing to see if ""$ComputerName"" is coming online..." -BackgroundColor $DefaultBackgroundColor
while($RemainingAttempts -gt 0) {
if(Test-Connection -ComputerName $ComputerName -Quiet -Count 1) {
Write-Host """$ComputerName""" -BackgroundColor Green -NoNewline
Write-Host " Is coming online...Skipping to offline one's"
break
} else {
Write-Host """$ComputerName""" -BackgroundColor Red -NoNewline
Write-Host " is Offline" -BackgroundColor Red -ForegroundColor Black -NoNewline
Write-Host ". Pausing for $SleepTimer minutes. Remaining attempts: $($RemainingAttempts - 1)"
Start-Sleep -Seconds ($SleepTimer * 60)
$RemainingAttempts--
}
}
if($RemainingAttempts -eq 0) {
Write-Host "Maximum number of attempts reached" -BackgroundColor $DefaultBackgroundColor
}
}
hope this helps!
Let me know if you have any question concerning the code.
KR
Guenther
Test-Connection returns an array of objects or a $null depending if the ip or hostname is live.
so its enough if you do this:
IF($online){
// if its alive
}
else
{
// if its not responding
}
another thing to point out is you dont have to
if(condition) {
}
elseif(condition) {
}
if you are testing "bool question-answer" you don't need the elseif. In your case, if you are checking the state of computer then you can't have more answers than 2 (yes/no). So the elseif is in this case redundant.
if(condition) {
// if the condition is true
}
else {
// if the condition is not true. you can't have the third option
}
Also your if condition If ($Online -NE "True") is checking if the variable $Online is not equal the exact string "true" or "True" since -ne is case insensitive condition operator.
You should check if the variable holds anything or holds $Null
While I know Guenther Schmitz has already got the green check, I wanted to follow up with some additional details and pointers.
Test-Connection with the -Quiet switch returns a [Boolean] type. This is a simple true/false. I like to be extra careful with comparing string values to Boolean values because you can get some odd results depending on how you frame your tests. For example:
'false' -eq $true
$true -eq 'false'
Top one equals false, bottom one equals true because PowerShell is converting the second object into the same type as the first, and a non-empty string is true. So my strong recommendation is to stick to typed comparisons. So the first code change I'd start with is:
if ($Online -eq $true) {
As #pandemic already mentioned, you're doing a simple comparison, so no need for elseif, just else.
The second issue you have is that there is nothing removing computers from your testing list. The way your code is written, if the last computer in your testing list is "down" then it'd go through all the computers again. If the last computer in the list is "up" then it'd quit the do|while loop, irregardless of how many computers you are testing and how many are down. You can verify this with a simple test of putting 1 known hosts, and 1 fake host in the test file. If the known good host is first, it'll keep looping until $AttemptsCounter reaches the maximum allowed. If you flip the list around, it'll bail out immediately after testing the known good host.
Your initial code looped through all the computers once then went back and started again. In Guenther's example, they loop one computer at a time, and check until it comes up, or bail out after exceeding the test count. I think Guenther's example was probably what you were after, but if you wanted to make it cycle through all the computers, and then test only those that had failed, I'd write something like this:
$comps = Get-Content -path 'c:\restarted.txt'
[int]$SleepTimer = 1
[int]$Attempts = 2
[int]$AttemptsCounter = 0
do {
Write-Host "Testing Network, attempt $($AttemptsCount + 1)"
$newComps = #()
foreach($comp in $comps) {
$online = Test-Connection -Quiet -Count 1 -ComputerName $comp
if ($online) {
Write-Host "$comp" -BackgroundColor Green -NoNewline
Write-Host " Is coming online...Skipping to offline one's"
} else {
$newComps += $comp
Write-Host "$comp" -BackgroundColor Red -NoNewline
Write-Host " is Offline" -BackgroundColor Red -ForegroundColor Black
}
}
$comps = $newComps
$AttemptsCounter++
Start-Sleep -Seconds ($SleepTimer * 60)
}
while (($comps.count -gt 0) -and ($AttemptsCounter -lt $Attempts))
if ($comps.Count -gt 0) {
Write-Host "Exceeded Attempts Counter" -BackgroundColor Red
}
In this code, it is taking the list of computers, and when it fails on a computer, it stuffs it into a temporary list for reuse.

How to configure a timeout for Read-Host in PowerShell

Like I said, this code works in PowerShell version 2, but not in PowerShell version 5.
function wait
{
$compte = 0
Write-Host "To continue installation and ignore configuration warnings type [y], type any key to abort"
While(-not $Host.UI.RawUI.KeyAvailable -and ($compte -le 20))
{
$compte++
Start-Sleep -s 1
}
if ($compte -ge 20)
{
Write-Host "Installation aborted..."
break
}
else
{
$key = $host.ui.rawui.readkey("NoEcho,IncludeKeyup")
}
if ($key.character -eq "y")
{Write-Host "Ignoring configuration warnings..."}
else
{Write-Host "Installation aborted..."
}}
The official documentation or Read-Host -? will tell that it's not possible to use Read-Host in that manner. There is no possible parameter to tell it to run with some kind of timeout.
But there are various other questions detailing how to do this in PowerShell (usually utilizing C#).
The idea seems to be to check whenever the user pressed a key using $Host.UI.RawUI.KeyAvailable and check that for the duration of your timeout.
A simple working example could be the following:
$secondsRunning = 0;
Write-Output "Press any key to abort the following wait time."
while( (-not $Host.UI.RawUI.KeyAvailable) -and ($secondsRunning -lt 5) ){
Write-Host ("Waiting for: " + (5-$secondsRunning))
Start-Sleep -Seconds 1
$secondsRunning++
}
You could use $host.UI.RawUI.ReadKey to get the key that was pressed. This solution probably would not be acceptable if you need more complex input than a simple button press. See also:
Windows PowerShell Tip of the Week - Pausing a Script Until the User Presses a Key
PowerTip: Use PowerShell to Wait for a Key Press (Hey, Scripting Guy!)
Seth, thank you for your solution. I expanded on the example you provided and wanted to give that back to the community.
The use case is a bit different here - I have a loop checking if an array of VMs can be migrated and if there are any failures to that check the operator can either remediate those until the checks clear or they can opt to "GO" and have those failing VMs excluded from the operation. If something other than GO is typed state remains within the loop.
One downside to this is if the operator inadvertently presses a key the script will be blocked by Read-Host and may not be immediately noticed. If that's a problem for anyone I'm sure they can hack around that ;-)
Write-Host "Verifying all VMs have RelocateVM_Task enabled..."
Do {
$vms_pivoting = $ph_vms | Where-Object{'RelocateVM_Task' -in $_.ExtensionData.DisabledMethod}
if ($vms_pivoting){
Write-Host -ForegroundColor:Red ("Some VMs in phase have method RelocateVM_Task disabled.")
$vms_pivoting | Select-Object Name, PowerState | Format-Table -AutoSize
Write-Host -ForegroundColor:Yellow "Waiting until this is resolved -or- type GO to continue without these VMs:" -NoNewline
$secs = 0
While ((-not $Host.UI.RawUI.KeyAvailable) -and ($secs -lt 15)){
Start-Sleep -Seconds 1
$secs++
}
if ($Host.UI.RawUI.KeyAvailable){
$input = Read-Host
Write-Host ""
if ($input -eq 'GO'){
Write-Host -ForegroundColor:Yellow "NOTICE: User prompted to continue migration without the blocked VM(s)"
Write-Host -ForegroundColor:Yellow "Removing the following VMs from the migration list"
$ph_vms = $ph_vms | ?{$_ -notin $vms_pivoting} | Sort-Object -Property Name
}
}
} else {
Write-Host -ForegroundColor:Green "Verified all VMs have RelocateVM_Task method enabled."
}
} Until(($vms_pivoting).Count -eq 0)
Also note that all this $Host.UI stuff doesn't work from the Powershell ISE.
To find out from within a script you could test for $Host.Name -eq "ConsoleHost". When true you can use the code from this topic. Otherwise you could use $Host.UI.PromptForChoice or any other way of showing a dialog box. With System.Windows.Forms.Timer you can then set a timer, and code to close the dialog box or form can be run when it expires.

Powershell - Checking Security Group Members against another list

A bit of context, I am trying to get a list of users from a security group, then check if any of those users do not have an assigned XenDesktop.
Please forgive me, I only started using Powershell yesterday so my formatting is off. It first grabs the users from the AD group then checks if that user has an assigned desktop, which works but I what I can't seem to get working is that if the user is found in that list, I want it to move onto the next username, instead it continues to check against every machine and then onto the final bits of code.
$checkusernames = Get-ADGroupMember "***AD security Group***" | Select SamAccountName
$desktops = get-brokerdesktop -DesktopGroupName Personal_WIN8 | Select MachineName, #{Name='AssociatedUserNames';Expression={[string]::join(“;”, ($_.AssociatedUserNames))}}
foreach ($username in $checkusernames.SamAccountName) {
foreach ($desktop in $desktops) {
If ($desktop.AssociatedUserNames -like "*$username*") {
write-host $username "is assigned to" $desktop.machinename
}
write-host $username "is not assigned to a desktop"
}
Write-host $username "is not assigned to anything"
pause
}
If you want to exit from a ForEach loop early you can do so with the Break keyword. For example:
$checkusernames = Get-ADGroupMember "***AD security Group***" | Select SamAccountName
$desktops = get-brokerdesktop -DesktopGroupName Personal_WIN8 | Select MachineName, #{Name='AssociatedUserNames';Expression={[string]::join(“;”, ($_.AssociatedUserNames))}}
foreach ($username in $checkusernames.SamAccountName) {
$machine = ''
foreach ($desktop in $desktops) {
If ($desktop.AssociatedUserNames -like "*$username*") {
$machine = $desktop.machinename
break
}
}
If ($machine) {
write-host $username "is assigned to" $desktop.machinename
} Else {
write-host $username "is not assigned to a desktop"
pause
}
}
This will stop the current cycle of the inner ForEach loop (once it has completed in it's entirety) without interrupting the ongoing cycle of the outer one.
Per the dicussion in the comments, i've also reorganised the code so that you only get a single output dependent on whether a desktop is matched to a user or not and it only pauses if it does not find a match.

Checking if Distribution Group Exists in Powershell

I am writing a script to quickly create a new distribution group and populate it with a CSV. I am having trouble testing to see if the group name already exists.
If I do a get-distributiongroup -id $NewGroupName and it does not exist I get an exception, which is what I expect to happen. If the group does exist then it lists the group, which is also what I expect. However, I can not find a good way to test if the group exists before I try to create it. I have tried using a try/catch, and also doing this:
Get-DistributionGroup -id $NewGroupName -ErrorAction "Stop"
which makes the try/catch work better (as I understand non-terminating errors).
Basically, I need to have the user enter a new group name to check if it is viable. If so, then the group gets created, if not it should prompt the user to enter another name.
You can use SilentlyContinue erroraction so that no exception/error shows:
$done = $false
while(-not $done)
{
$newGroupName = Read-Host "Enter group name"
$existingGroup = Get-DistributionGroup -Id $newGroupName -ErrorAction 'SilentlyContinue'
if(-not $existingGroup)
{
# create distribution group here
$done = $true
}
else
{
Write-Host "Group already exists"
}
}
This should do the trick:
((Get-DistributionGroup $NewGroupName -ErrorAction 'SilentlyContinue').IsValid) -eq $true