Powershell form: how can I find controls based on name-property - winforms

How can I list all the controls in my form where (name)-property begins with something spesific and then use it in a foreach?
I have multiple groupboxes under multiple tabpages where the name starts with gbs (Example: gbs1, gbs2, gbs3 ++). Now I want to disable all the groupeboxes that begins with gbs* at say a click of a botton without having to list all the groupboxes manually.
I've tried to look it up, but I can't find good documentation on it. I might be searching for the wrong words...
I'm guessing this is a start, and it's as far as I've come, but I'm not sure where to go from here or if I'm way off;
$list = #($MainForm.Controls.Find -like 'gbs*')
foreach ($item in $list){$item.enabled = $false} #Just a example of what I'm thinking
Purpose:
I am creating a .exe form that is going to automate active directory. The form contains a bunch of settings, and all of them is within these groupboxes. I’m going to have a ‘edit-mode’, so that when it’s active - the groupboxes are enabled. When not, disabled.
Solution, thanks to help from #Clijsters :
#Get all the tabpages
$script:tabpages = $tabTasks.Controls | Where-Object { $_.Name -like "tp*" } | select Name, Controls
$script:tabpages += $tabSettings.Controls | Where-Object { $_.Name -like "tp*" } | select Name, Controls
#Get groupboxes in tabpages
$script:groupboxes = [System.Object]$tabpages.Controls
#Disable
$groupboxes | Where-Object { $_.Name -like "gbs*" } | ForEach-Object { $_.Enabled = $true }

For finding Items in a list, Where-Object is your CmdLet of choice!
In your case something like
$list = $MainForm.Controls | Where-Object {$_.Name -like "gbs*"}
will work fine. You can store the result in a variable (like shown above) and use it in foreach or pipe the resulting list directly to ForEach-Object and process it there like below:
$MainForm.Controls | Where-Object {$_.Name -like "gbs*"} | ForEach-Object {Do Something}

Related

Rename AD groups using two PowerShell arrays

I'm trying to rename existing AD groups in this way.
AD groups starting # to be renamed to the same name without #. For example , I have #dl1 and I wish to get it renamed dl1 (omitting #)
Im trying to rename following four users first.
I have written two arrays, in this manner. ($myArray and $myArray2).
$myArray =#(
$data = Get-ADGroup -Filter {name -like "#*"} |select samaccountname
$data.samaccountname | foreach {$_.split("#")[1]
}
)
$myArray2 =#(
$assdf=Get-ADGroup -Filter {name -like "#*"}
$myArray2 =#($assdf)
$num=0
foreach($a in $assdf)
{
$myArray2[$num]
$num=$num+1
}
)
If I print $myarray it gives exact results, that I wish, in this way.
and also if I print $myarray2 it gives the desired results in this way,
the missing piece of the puzzle is combining those two arrays to run the final command that is
set-adgroup -identity (members indide $myArray2) -samaccountname (members indide $myArray)
For hours, I have tried numerous methods to get set-adgroup .. using for each loop etc.
for example,
$a=0
foreach ($item in $myArray2)
{
$nameto_replace=$myArray[$a]
Set-adgroup -identity $item.samaccountname -samaccountname $nameto_replace
$a=$a+1
}
Can anyone please shed some light, please? I am totally out of ideas now. thanks in advance
There is no need to perform Get-ADGroup twice, where you can use it once and loop over the results in a ForEach-Object loop:
Updated as per Aravinda's helpful observation
Get-ADGroup -Filter "Name -like '#*'" | ForEach-Object {
$newName = $_.Name.TrimStart('#')
Write-Host "Renaming group $($_.Name).. to '$newName'"
# replace only the SamAccountName
$_ | Set-ADGroup -SamAccountName $newName
# or replace multiple properties at the same time.
# You need to use the LDAP names here, so mind the casing !
# See http://www.selfadsi.org/group-attributes.htm
# $_ | Set-ADGroup -Replace #{sAMAccountName = $newName; displayName = $newName}
}
You can limit the search to a specified OU if you want by adding the OU's DistinguishedName with the -SearchBase parameter
Theo's answer is fantastic!
Following is the one finally I used derived from theo's answer.
Get-ADGroup -Filter "Name -like '#*'" | ForEach-Object {
$newName = $_.Name.TrimStart('#')
$_ | Set-ADGroup -Replace #{sAMAccountName = $newName;displayName = $newName}
$_ | Rename-ADObject -NewName $newName
}
If you try using set-adgroup to change 'name' and 'CN' and it gives below error.
"Set-ADGroup : The directory service cannot perform the requested operation on the RDN attribute of an object"
To change multiple attributes, especially including Name and CN, combination of Rename-ADObject and Set-ADGroup can be used.

Cannot add items to Powershell array

I'm relatively new to Powershell but haven't been able to find an answer online.
I'm trying to get the number of emails per disabled user in exchange 2010, but also need to get the user's title form AD as the organization groups users by type using the Title attribute in AD
I've written the following but I'm unable to get the data I need, it just returns Length and numbers to the CSV file e.g.
"length"
"10"
"3"
"34"
If I leave $title out of the assignment of $Disabled+= the user's name and item count is added to the csv file, but I really need the title also. Can anyone point out where I'm going wrong.
Import-Module ActiveDirectory
$i=0
$disUsers = Get-ADUser -Filter * -SearchBase "ou=User Disabled Accounts,dc=test,dc=com" -Properties SamAccountName,Title
$Disabled = #()
$disUsers | Foreach-Object{
$sam = $_.SamAccountName
$title = $_.Title
$mailDetail=Get-MailboxStatistics $sam | Select -Property DisplayName,ItemCount
$Disabled += $title, $mailDetail
$i++;
}
$Disabled | Export-Csv -Path $env:userprofile\desktop\DisabledADUserTitlewithMailbox.csv -NoTypeInformation
Working with the code provided by Steve unfortunately gives the following errors
Exception calling "Add" with "2" argument(s): "Item has already been added. Key in dictionary: 'ADCDisabledMail' Key being added: 'ADCDisabledMail'" ...
Exception calling "Add" with "2" argument(s): "Key cannot be null. Parameter name: key"...
EDIT
With help from Steven I was able to get this working with the following
'Import-Module ActiveDirectory'
$i=0
$disUsers=Get-ADUser -Filter {mailNickName -like '*'} -SearchBase "ou=User Disabled Accounts,dc=test,dc=com" -Properties SamAccountName,Title
$dis2 = $disUsers.count
$DisabledUser = #()
$disUsers | Foreach-Object{
Write-Host "Processing record $i of $dis2"
$sam = $_.SamAccountName
$title = $_.Title
$mailDetail=Get-MailboxStatistics $sam | Select-Object DisplayName, #{ Name = 'Title'; Expression = {$title}}, ItemCount
$DisabledUser+= $mailDetail
$i++;
}
$DisabledUser | Export-Csv -Path $env:userprofile\desktop\DisabledADUserTitlewithMailbox.csv -NoTypeInformation
It sounds like what you are really trying to do is relate data to create a small report. You are dealing with data coming from different commands so you need a property to join on. In this case I would look at the LegacyExchangeDN AD attribute and the LegacyDN property returned by Get-MailboxStatistics. The code might look something like:
$DisabledUsers = #{}
Get-ADUser -SearchBase 'ou=User Disabled Accounts,dc=test,dc=com' -Filter * -Properties 'Title','legacyExchangeDN' |
ForEach-Object{ $DisabledUsers.Add( $_.legacyExchangeDN, $_ ) }
$DisabledUsers.Values.SamAccountName |
Get-MailboxStatistics |
Select-Object DisplayName, ItemCount, #{ Name = 'Title'; Expression = { $DisabledUsers[$_.LegacyDN].Title } }
This will output something like:
DisplayName ItemCount Title
----------- --------- -----
Mr. Smith 113576 Executives
If you would rather it go directly to a CSV file simply add the Export-CSV command after the Select-Object command, like below:
$DisabledUsers = #{}
Get-ADUser -SearchBase 'ou=User Disabled Accounts,dc=test,dc=com' -Filter * -Properties 'Title','legacyExchangeDN' |
ForEach-Object{ $DisabledUsers.Add( $_.legacyExchangeDN, $_ ) }
$DisabledUsers.Values.SamAccountName |
Get-MailboxStatistics |
Select-Object DisplayName, ItemCount, #{ Name = 'Title'; Expression = { $DisabledUsers[$_.LegacyDN].Title } } |
Export-CSV -Path $env:userprofile\desktop\DisabledADUserTitlewithMailbox.csv -NoTypeInformation
I would've used Get-User from the Exchange Management Shell, however it doesn't have the LegacyExchangeDN as a returned property. It does have SamAccountName, but using it would've forced me to bridge everything through Get-Mailbox. At any rate, this is a very common technique to use a hash table to reference related values in a different collection.
I'm sure some additional work will be needed to get the report just right.
An aside, try to avoid using the += operator to append arrays. The best way to get an array is to let PowerShell provide it as I did above. However, if you can't get around it the most common alternative is an ArrayList. Like most things there are several ways to go about it below is just 1 example.
# To create:
$ArrList = [Collections.ArrayList]#()
#To Add a value:
[Void]$ArrList.Add( 'ValueOrObjectHere' )
Note: Documentation / discussion of += and ArrayList's are easy to
find with the Google machine...
Update:
Addressing Errors Noted in most recent edit:
The first error is basically impossible. Forgive me but I must assume you made some mistake to generate this error. LegacyExchangeDN should always start with '/o=...' and they key cited by the error was 'ADCDisabledMail' . Also LegacyExchangeDNs are naturally unique in Active Directory, so there's almost no chance you'd have a duplicate. As such, I made no effort, and none is warranted, to prevent such an unlikely error.
Note: If you are repeatedly testing the code you have to recreate the
hash, $DisabledUsers = #{} else the hash will exist from the
previous run and duplicate key errors are a certainty...
The second error, they 'key cannot be null' might be due to non-mailbox enabled AD accounts in the referenced OU effectively causing the LegacyExchangeDN attribute to be null for those users. Hence, null key.... You can avoid that by modifying the filter to only return mail enabled users:
$disUsers = Get-ADUser -Filter { mailNickName -like '*' } -SearchBase "ou=User Disabled Accounts,dc=test,dc=com" -Properties SamAccountName,Title
Note: For reference, mailNickName is the alias propertry typically
returned with Get-Mailbox

Recursively delete folders where folder name contains a number

Good afternoon all,
I am attempting to delete folders at a specific location containing a number in the name, which can be any number in the array.
$fso = New-Object -com "Scripting.FileSystemObject"
$Versionarray = (13..20)
$folder =
$fso.GetFolder("$env:USERPROFILE\appdata\local\Microsoft\OneDrive")
foreach ($subfolder in $folder.SubFolders)
{
If ($subfolder.Name -match "$Versionarray")
{
remove-item $subfolder.Path -Verbose
}
}
Please see an example of the following folders it will sift through below:
18.172.0826.0010
18.172.0826.0010_2
18.172.0826.0015
18.172.0920.0015
18.172.0920.0015_1
logs
settings
setup
If I change the "VersionArray"array to the variable "18" instead, it will start to remove the folders. It doesn't appear to be going through each number of the array. I need it to be an array to future-proof the script as the number represents a version of OneDrive.
Thank you for looking over this.
Going from your initial idea to have a list of items that must be contained in the subfolder name, we can do a pipeline like this:
$Versionarray = 13..20
Get-ChildItem "$env:LOCALAPPDATA\Microsoft\OneDrive" -Recurse | Where-Object {
$item = $_
$item -is [System.IO.DirectoryInfo] -and (
$Versionarray | Where-Object { $item.Name.Contains($_) }
)
} | Remove-Item -WhatIf
Notes:
Get-ChildItem returns all subfolders and files in a folder. Drop -Recurse if you don't need that.
Where-Object filters any list of objects according to a condition. Any result other than 0, $false, $null, or the empty string/empty list will be considered $true. It's not necessary to actually return $true, as long as anything is returned at all.
$_ is the "current item" in the pipeline
$foo -is [Fully.Qualified.ClassName] checks if an object is of a certain class. In this case, we only want to look at System.IO.DirectoryInfo objects and ignore all files.
$Versionarray | Where-Object { $item.Name.Contains($_) } filters the $Versionarray down to those elements that are contained in the folder name. You could use .StartsWith() or any other method of .NET strings in its place.
Any object that "survives" the Where-Object filter is passed to Remove-Item
-WhatIf performs a dry-run, drop it when you're sure the right thing will happen.

Test-Connection $False will not convert to ArrayList

Currently working on making a new report that will be generated with PowerShell. Using PowerShell to build a HTML email. I have one other report working fine but ran into an unexpected issue on this one.
The below code is just s sample from the script I am still building. Still adding pieces to the script but testing it as I move forward. I added a Test-Connection to see if a computer was responding or not and lost the ability to build an array.
My final goal with this report is to import a list of names from a file and then loop over all of the computers to see if they are pinging and gather some information from them using Get-WMIObject, etc.
The below code will replicate the issue I am having but I am not sure how to solve it. I've narrowed down the issue to when Test-Connection returns 'False'. On line 26 I am filtering for just results that returned a 'False' on Test-Connection to save them into its own array so that I can use that array in a different part of my code to build the HTML table/HTML to send out the email.
Only the flipside, if I tell it to look for only 'True', it will save into the array without issue.
This is the error that PowerShell is giving when doing filtering by 'False'.
Cannot convert value "#{Computer_Name=Computer1; Ping_Status=False}" to type "System.Collections.ArrayList". Error: "Cannot convert the "#{Computer_Name=Computer1 Ping_Status=False}" value of type "Selected.System.Management.Automation.PSCustomObject" to type "System.Collections.ArrayList"."
Please let me know if there is any other information that I can provide. I've been stuck on this one for a while. Co-workers are even say this is a weird one.
Is there something unique about the way Test-Connection return a 'False'?
CLS
[string]$ErrorActionPreference = "Continue"
[System.Collections.ArrayList]$Names = #(
"Computer1"
"Computer2"
)
[System.Collections.ArrayList]$WMI_Array = #()
[System.Collections.ArrayList]$Ping_Status_False = #()
foreach ($Name in $Names) {
[bool]$Ping_Status = Test-Connection $Name -Count 1 -Quiet
$WMI_Array_Object = [PSCustomObject]#{
'Computer_Name' = $Name
'Ping_Status' = $Ping_Status
}
$WMI_Array.Add($WMI_Array_Object) | Out-Null
}
$WMI_Array | Format-Table
[System.Collections.ArrayList]$Ping_Status_False = $WMI_Array | Where-Object {$_.Ping_Status -eq $false} | Select-Object Computer_Name, Ping_Status
$Ping_Status_False
The problem is not Test-Connection but that this statement
$WMI_Array | Where-Object {$_.Ping_Status -eq $false} | Select-Object Computer_Name, Ping_Status
produces just a single result. Which is not an array, and can thus not be converted to an ArrayList. The behavior is identical when you filter for $_.PingStatus -eq $true with just a single matching object, so I suspect that you had either more than one successfully pinged host or none at all when you tested that condition and it didn't throw the same error.
You could mitigate the problem by wrapping the statement in the array subexpression operator:
[Collections.ArrayList]$Ping_Status_False = #($WMI_Array |
Where-Object {$_.Ping_Status -eq $false} |
Select-Object Computer_Name, Ping_Status)
Or, you could simply drop all the pointless type-casting from your code:
$ErrorActionPreference = "Continue"
$Names = 'Computer1', 'Computer2'
$WMI_Array = foreach ($Name in $Names) {
[PSCustomObject]#{
'Computer_Name' = $Name
'Ping_Status' = [bool](Test-Connection $Name -Count 1 -Quiet)
}
}
$WMI_Array | Where-Object { -not $_.Ping_Status }

ACL "fuzzy" comparision

I'm trying to compare ACLs on a folder with a reference set of ACLs, and then list any exceptions. The "fuzzy" part of the equation is that I want to be able to disregard any unknown SID. So creating a reference folder with the perms I want to test won't work to use Compare-Object between it and my test folder.
The underlying scenario is that I am cleaning up old user directories where the actual user account has been deleted (this is where the non-resolved SID comes in). By default, the folders include perms for Administrator and the like, which I don't care about. There are some folders, however, where another user has been granted explicit permissions, and I want to capture these. Unfortunately, there aren't any shortcuts I can use to check: e.g. -IsInherited or the like to exclude ACLs I don't care about.
Per the below, I can dump the ACLs out into an array
$acl = get-acl f:\user_folder
$access = $acl.Access | ForEach-Object { $_.identityReference.value }
$access
BUILTIN\Administrators
MYDOMAIN\JBLOGGS
S-1-5-21-4444444444-9999999-1111111111-74390
MYDOMAIN\Domain_Group ###Yes, the group has an underscore in the name
I can create another array of the users I want to ignore, including a partial string to match any unresolved SID.
$defaults = #("BUILTIN\Administrators","MYDOMAIN\DomainGroup","S-1-5-21")
So how do I compare my $defaults array with the $access array and output only the exceptions like "MYDOMAIN\JBLOGGS"?
I'm trying a foreach, but I'm stumped about grabbing that exception. The following still outputs the SID I want to avoid. I'm hoping to also avoid too many nested "IFs".
$access | ForEach { If ($defaults -notcontains $_) { Write-Output $_ } }
MYDOMAIN\JBLOGGS
S-1-5-21-4444444444-9999999-1111111111-74390 #Do not want!
If I put the wildcard $_* into the -notcontains, I get the whole contents of $access again.
I'd do something like this:
$defaults = 'BUILTIN\Administrators', 'MYDOMAIN\DomainGroup', 'S-1-5-21*'
$acl.Access | Where-Object {
$id = $_.IdentityReference
-not ($defaults | Where-Object { $_ -like $id })
} | Select-Object -Expand value
$defaults | Where-Object { $_ -like $id } does a wildcard match of the given identity against all items of $defaults. The wildcard * at the end of S-1-5-21* allows to match all strings starting with S-1-5-21. The negation -not inverts the result so that only identities not having a match in $defaults pass the filter.
give the users you want to ignore some right on a dummy folder, get the acl of that folder and then compare whith the acl of your actual folder
$genericACL = get-acl c:\temp\dummy
$folderacl = get-acl f:\user_folder
$exceptions= $folderacl.Access.identityreference.value |?{ ($_ -notin $genericACL.access.identityreference.value) -and ($_.strartswith('S-1-5-21') -eq $false)) }
In the end, it was fairly simple, thanks to the help above.
I managed to omit the fact in the original question where I required it to work in Powershell v2.
$defaults = #("BUILTIN\Administrators","MYDOMAIN\DomainGroup")
$acl = get-acl $folder
$access = $acl.Access | ForEach-Object { $_.identityReference.value }
# check that no other account still has access to the folder
$access | ForEach {
If ($defaultACL -notcontains $_ -and $_ -notlike 'S-1-5-21*') {
write-output "Extra perms:$user $_"
}

Resources