I have a function that for some reason, just seems to only display $Properties once, no matter how many users are passed to the script. Tried to throw in just the $user passed into the script to display but, that too only displays once. Not too sure why it's doing this as I have exhausted ideas on what it may be:
Function Set-DRAUserProperty {
Param (
[Parameter(Mandatory=$true,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[string[]]$UserName,
$Company,
$Department,
$Name,
$O,
$PhysicalDeliveryOfficeName,
$TelephoneNumber,
$Title
)
DynamicParam
{
if ($UserName.Split(',').Count -le 1) {
$displayNameAttribute = New-Object System.Management.Automation.ParameterAttribute
$displayNameAttribute.Mandatory = $false
$attributeCollection = new-object System.Collections.ObjectModel.Collection[System.Attribute]
$attributeCollection.Add($displayNameAttribute)
$displayNameParam = New-Object System.Management.Automation.RuntimeDefinedParameter('DisplayName', [string], $attributeCollection)
$paramDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
$paramDictionary.Add('DisplayName', $displayNameParam)
$paramDictionary
}
}
Begin
{
$OrgBoxOu = "*OU=xxxxxxxxx"
$Parameters = $PSBoundParameters
$Properties = #{}
}
Process
{
foreach ($User in $UserName)
{
try {
$SelectedUser = Get-ADUser -Identity $User -Properties DisplayName
if ($SelectedUser) {
$Parameters.GetEnumerator() | Where-Object -FilterScript {$_.Key -ne 'UserName'} |
ForEach-Object -Process `
{
if ($_.Key -eq 'DisplayName' -and $SelectedUser.DistinguishedName -like $OrgBoxOu) {
if ('FirstNamePreferred' -in $Properties.Keys) {
Continue
}
else {
$Properties.Add($_.Key, $_.Value)
$Properties.Add('FirstNamePreferred', $_.Value)
}
}
else {
if ($_.Key -in $Properties.Keys) {
Continue
}
else {
$Properties.Add($_.Key, $_.Value)
}
}
}
$Properties.GetEnumerator()
#Set-DRAUser -Identifier $SelectedUser.DistinguishedName -Properties $Properties #DRA_Server_Splat -ErrorAction Stop
}
else {
Write-Host -Object "No valid member selected!"
}
}
catch {
Write-Host -Object "ERROR: $($_.Exception.Message)" -ForegroundColor Red -BackgroundColor Black
continue
}
}
}
End { }
}
When running the function,
Set-DRAUserProperty -UserName Abe,Abe -Company ssc
. . . the output is only displayed once:
Name Value
---- -----
Company ssc
What i'm tring to achieve is having the values be splatted onto another cmdlet, but it won't work as long as my $Properties hashtable only displays once. So expected output would be it display the hashtable for each user that's been passed to the function:
Name Value
---- -----
Company ssc
Name Value
---- -----
Company ssc
Just looking for some fresh eyes that can point me in the right direction and/or, shed some light on what might be happening (wrong). Not sure what could be causing the issue so can't pin-point it to a specific code section.
Please edumecate me:)
Solution was to define the hashtable inside the foreach loop instead of inside the begin { ... } block:
foreach ($User in $UserName)
{
$Properties = #{}
....
....
}
But if you want to understand why it was not working before?, this is your hint:
if ($_.Key -in $Properties.Keys) {
'{0} is in $Properties.Keys :)' -f $_.Key
Continue
}
Which yields:
Name Value
---- -----
Company ssc
Company is in $Properties.Keys :)
Company is in $Properties.Keys :)
And this only happens because $Properties is the same on each iteration (because it was defined in the begin { ... } block).
Related
The below function is supposed to add the the passed parameters, and values, to a hashtable for later use when setting the actual properties but. . . for a reason I can't understand, the hashtable is only displayed once, with just one set of properties.
I'm trying to have it splat all the values passed for each user, but it only does it for one.
Function Change-DRAUserProperty {
Param (
[Parameter(Mandatory=$true,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[string[]]$UserName,
$Company,
$Department,
$DisplayName,
$Name,
$O,
$PhysicalDeliveryOfficeName,
$TelephoneNumber,
$Title
)
Begin
{
$OrgBoxOu = "*OU=XX, DC=XX"
$Parameters = $PSBoundParameters
$Properties = #{}
}
Process
{
foreach ($User in $UserName)
{
try {
$SelectedUser = $User
if ($SelectedUser) {
$Parameters.GetEnumerator() | Where-Object Key -ne 'UserName' |
ForEach-Object -Process `
{
if ($_.Key -eq 'DisplayName' -and $SelectedUser.DistinguishedName -notlike $OrgBoxOu) {
$Properties.Add('FirstNamePreferred', $_.Value)
}
$Properties.Add($_.Key, $_.Value)
}
$Properties
#Set-DRAUser -Identifier $SelectedUser.DistinguishedName -Properties $Properties #DRA_Server_Splat -ErrorAction Stop
}
else {
Write-Host -Object "No valid member selected!"
}
}
catch {
Write-Host -Object "$($_.Exception.Messge)" -ForegroundColor Red -BackgroundColor Black
continue
}
}
}
End { }
}
Using the command line arguments: Change-DRAUserProperty -UserName Abe, Abe2, Abe3 -Company SSC -Department USSF
. . .only displays:
Name Value
---- -----
Department USSF
Company SSC
Instead of one per user passed (what the results should be):
Name Value
---- -----
Department USSF
Company SSC
Name Value
---- -----
Department USSF
Company SSC
Name Value
---- -----
Department USSF
Company SSC
No too sure what I may be doing wrong, may I have someone else take a look over and point out my mistakes?
Am I going crazy guys?
I'm working on a PowerShell script for some work use, and am having trouble getting a function it's data directly to a variable. Below is the function and my test:
function validateInput {
Param(
[string]$Text = 'Put text here'
)
do {
try {
$numOk = $true
$GetMyANumber = Read-Host "$text"
if ($GetMyANumber -gt 3){
$numOK = $false
}
} catch {
$numOK = $false
}
if ($numOK -ne $true) {
cls
Write-Host "Please enter a valid number" -Foreground Red
}
} until (($GetMyANumber -ge 0 -and $GetMyANumber -le 3) -and $numOK)
}
$firstName = validateInput($firstNamePrompt)
$lastName = validateInput ($lastNamePrompt)
Write-Host "First name length is $firstname"
Write-Host "Last name length is $lastname"
My understanding is that the in the last few lines, the function SHOULD assign it's output to the variables $firstName and $lastName, but I output that, they are blank. I'm sure I'm missing something obvious here, but can anyone let me know what I'm screwing up?
This only prints the last server in the list, I'm looking to get all servers and print to screen
$machines = (Get-BrokerMachine -AdminAddress $adminaddress -DesktopGroupName $deliverygroup | Select-Object DNSname).DNSname
foreach($machine in $machines){
$machinelist = Get-BrokerMachine -HostedMachineName $machine
if($machinelist.InMaintenanceMode -eq $true){
$status = "$machine is in maintenance mode"
}else {
$status = "$machine is not in maintenance mode"
}
}
Write-Host $status
Here is a more PowerShell-like approach (not tested):
Get-BrokerMachine -AdminAddress $adminaddress -DesktopGroupName $deliverygroup | ForEach-Object {
$machineName = $_.DNSName
[PSCustomObject] #{
"MachineName" = $machineName
"MaintenanceMode" = (Get-BrokerMachine -HostedMachineName $machine).InMaintenanceMode
}
} | Export-Csv "C:\whatever\results.csv" -NoTypeInformation
$Status is constantly being overwritten by the current machine in your list.
You're looking for:
$Status+=
As opposed to:
$Status=
You'll also want to explicitly state that $Status will be an array at the beginning like so:
$Status=#()
Or when you create the variable and omit the line at the beginning.
[array]$Status +=
Otherwise, you'll get results that run together as it will be treated as a [String]
another funky mode :
function get-BrokerMachineMode
{
param (
[Parameter(Mandatory = $true)]
[string[]]$machines
)
begin
{
$ErrorActionPreference = 'Stop'
Add-Type -Language CSharp #"
public class BrokenBroker {
qpublic System.String MachineName;
public System.String MaintenanceMode;
public BrokenBroker (string MachineName, string MaintenanceMode)
{
this.MachineName = MachineName;
this.MaintenanceMode = IsInMaintenanceMode;
}
}
"#
$status = #()
Write-Verbose "Created objects..."
}
process
{
try
{
$machines = (Get-BrokerMachine -AdminAddress $adminaddress `
-DesktopGroupName $deliverygroup | Select-Object DNSname).DNSname
foreach ($machine in $machines)
{
Write-Verbose "Checking machine: $machine"
$machinelist = Get-BrokerMachine -HostedMachineName $machine
if ($machinelist.InMaintenanceMode -eq $true)
{
$status += New-Object BrokenBroker($machine, $true)
}
else
{
$status += New-Object BrokenBroker($machine, $false)
}
}
}
catch
{
Write-Error $error[0].Exception.Message
}
$status
}
end
{
Write-Verbose "Done"
}
}
this is a function you just must to load then you can launch it just by using this command:
$computers = get-content = {PATH TO TXT FILE}
$list = get-BrokerMachineMode -machines $computers -Verbose
Hi apologies if this has been asked before. If so
I have function that builds a object array of group members. I can see it works fine inside the function but the return object is has exactly double the members - tried an ArrayList and that is even worse. Can somebody please explain what is going on....
function Get-MsolGroupMembers
{
[CmdletBinding()]
param
(
[Parameter(Mandatory=$true, Position=0)]
[string]
$SearchString
)
$groups = Get-MsolGroup -SearchString $SearchString -MaxResults 1
$retObjs = #()
Write-Host -fore Yellow $groups.Count 'Group(s) found'
foreach ($group in $groups)
{
$groupGUID = $group.ObjectId
$groupDisplayName = $group.DisplayName
$groupEmail = $group.EmailAddress
$groupType = $group.GroupType
$groupMembers = Get-MsolGroupMember -GroupObjectId $groupGUID -All
foreach ($groupMember in $groupMembers)
{
$Properties = #{"GroupDisplayName"=$groupDisplayName;
"GroupEmail"=$grouEmail;
"GroupType"=$groupType;
"MemberDisplayName"=$groupMember.DisplayName;
"MemberEmail"=$groupMember.EmailAddress;
"MemberType"=$groupMember.GroupMemberType}
$Obj = New-Object -TypeName PSObject -Property $Properties
Write-Output $Obj | select GroupDisplayName,GroupEmail,GroupType,MemberDisplayName,MemberEmail,MemberType
$retObjs += $Obj
}
return $reObjs;
}
}
$members = Get-MsolGroupMembers -SearchString 'My Test Group'
$members.Count
Sure, this is easy. You're outputting everything twice. Once with the Write-Output line, and then again with the return line. PowerShell functions return anything to the pipeline that is not specifically redirected (such as with Write-Host or Export-Csv), so both of those commands essentially do the same thing, which is where your doubling comes from. Remove one or the other and you'll be all set.
In my PowerShell script I try to do some error handling. However, I'm depending on an advanced function that uses the Try/Catch clauses. So once in a while the code block in the function fails and goes to the Catch clause after generating an error. At this point the variable $Error is filled with one error.
If I then consult within my script the variable $Error it tells me there's one record, which is correct. But I would like to know if it's possible to only delete the last error within the function in the Catch clause? So I can keep my $Error variable clear for the script errors.
The problem is within Get-ADTSProfileHC. I tried to delete the last error with $Error[0] | Remove-Item but it failed.
The function:
Function Get-ADusersHC {
[CmdletBinding(SupportsShouldProcess=$True)]
Param(
[Parameter(ValueFromPipelineByPropertyName=$true,ValueFromPipeline=$true,Position=0)]
[String[]] $OU
)
Begin {
Function Get-ADOUNameHC {
$CanonicalName = $_.CanonicalName
[System.Collections.ArrayList]$Pieces = $CanonicalName.split(“/”)
$Pieces.Remove($Pieces[-1])
$OU = $Pieces -join '\'
$OU -replace ($Pieces[0],$Pieces[0].ToUpper())
}
Function Get-ADManagerDisplayNameHC {
$m = Get-ADObject -Identity $_.manager -Properties displayName,cn
if($m.ObjectClass -eq "user") { $m.displayName } Else{ $m.cn }
}
Function Get-ADTSProfileHC {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true,Position=0)]
[String] $DistinguishedName,
[parameter(Mandatory=$true,Position=1)]
[ValidateNotNullOrEmpty()]
[ValidateSet('UserProfile','AllowLogon','HomeDirectory','HomeDrive')]
[String]$Property
)
Begin {
$User = [ADSI]"LDAP://$DistinguishedName"
}
Process {
Try {
Switch ($Property) {
'AllowLogon' {if ($($User.psbase.InvokeGet('allowLogon')) -eq '1'){$True}else{$False}}
'HomeDirectory' {$User.psbase.InvokeGet('TerminalServicesHomeDirectory')}
'HomeDrive' {$User.psbase.InvokeGet('TerminalServicesHomeDrive')}
'UserProfile' {$User.psbase.InvokeGet('TerminalServicesProfilePath')}
}
}
Catch {
# When we receive an error, it means the field has never been used before and is blank
# this is due to an error in the AD (same problem with the Quest CmdLet), AllowLogon is
# always 'TRUE' but we don't set it because we can't read it sometimes so we write 'blanks'
Write-Output $null
}
}
}
}
Process {
Foreach ($_ in $OU) {
Write-Verbose "Function Get-HCADusersNoManager > OU: $_"
Write-Verbose "Function Get-HCADusersNoManager > Manager field empty"
Get-ADUser -SearchBase $_ -Filter 'SAMAccountName -eq "shenn"' -Properties * |
#Get-ADUser -SearchBase $_ -Filter * -Properties * |
Foreach {
$Properties = ([Ordered] #{
"Creation date" = $_.whenCreated;
"Display name" = $_.displayName;
"CN name" = $_.name;
"Last name" = $_.sn;
"First name" = $_.givenName;
"Logon name" = $_.sAMAccountName;
"Manager" = if($_.manager){Get-ADManagerDisplayNameHC};
"Employee ID" = $_.EmployeeID;
"HeidelbergcCement Billing ID" = $_.extensionAttribute8
"Type of account" = $_.employeeType;
"OU" = Get-ADOUNameHC;
"Notes" = $_.info -replace "`n"," ";
"E-mail" = $_.EmailAddress;
"Logon script" = $_.scriptPath;
"TS User Profile" = Get-ADTSProfileHC $_.DistinguishedName 'UserProfile';
"TS Home directory" = Get-ADTSProfileHC $_.DistinguishedName 'HomeDirectory';
"TS Home drive" = Get-ADTSProfileHC $_.DistinguishedName 'HomeDrive';
"TS Allow logon" = Get-ADTSProfileHC $_.DistinguishedName 'AllowLogon'
})
$Object = New-Object -TypeName PSObject -Property $Properties
Write-Output $Object
}
}
}
}
Two easy ways to do this:
$error.Remove($error[0])
$Error.RemoveAt(0)
Don't forget to check there is an error first.
$Error.Remove($error[$Error.Count-1])
If errors variable is empty, you will not get any exception
I hope it helps