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
Related
Hello everyone i need this script to work and i'm stuck with a error.
I need to have a dynamic script with user choice and linking the mailbox we enter in first step to the HR choice.
In the csv part we have a group distribution list
Error says that i can not do the add-distributiongroupmember and i don't know why
can someone help me on this one ?
(I just do the 0 part choice because i need this one to work before doing the other choice)
code below
Connect-ExchangeOnline
$User = Read-Host "Enter Name of the mailbox to add"
Try {
$Mbx = Get-Mailbox -Identity $User -ErrorAction Stop | Select -ExpandProperty PrimarySmtpAddress}
Catch {
Write-Host "No mailbox can be found called" $User; break }
$Services = [System.Management.Automation.Host.ChoiceDescription]::new('&Services')
$Services.HelpMessage = 'Get running services'
$HR = New-Object System.Management.Automation.Host.ChoiceDescription '&HR', 'Get running HRDL'
$Legal = New-Object System.Management.Automation.Host.ChoiceDescription '&Legal', 'Get running LegalDL'
$Quit = New-Object System.Management.Automation.Host.ChoiceDescription '&Quit', 'Quit menu'
$options = [System.Management.Automation.Host.ChoiceDescription[]]($HR, $Legal, $Quit)
$Result = $host.UI.PromptForChoice('Task menu', 'Select a Department', $options , 0 )
$DLs = Import-Csv -Path "C:\Users\GregorySemedo\Desktop\Script\DL\DL-HR.csv"
switch($Result)
{
0 { ForEach ($DL in $DLs) {
Try {
Add-DistributionGroupMember -Identity $DL."HR" -Member $Mbx -ErrorAction Continue }
Catch {
Write-Host "Couldn't add" $Mbx "to DL" (Get-DistributionGroup -Identity $DL."HR").DisplayName }
If($?)
{
Write-Host $User Succesfully added -ForegroundColor Green
}
Else
{
Write-Host $User - Error occurred -ForegroundColor Red
}}
}
}
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).
I have been developing AzureAD licence script based on AD Group. So, Find users with a direct assigned, find them in AD, evaluate what group they should be a member of, add them to licensing group. I have hashtable with multiple values $SKUToGroupRev. I can not match hashtable with multiple values with if($ADGroup = $SKUToGroupRev[$SKU.SkuId]) .
From what I want to do :
if there are 18181a46-0d4e-45cd-891e-60aabd171b4e and 0c266dff-15dd-4b49-8397-2bb16070ed52 inside SKUs variable for below command then I will add AD group related to the inside hashtable such as O365_E1_Users
OR
if there are 6fd2c87f-b296-42f0-b197-1e91e994b900 and 0c266dff-15dd-4b49-8397-2bb16070ed52 inside SKUs variable for below command then I will add AD group related to the inside hashtable such as O365_E3_Users
e.g:
# Get licensed SKUs for the user
$aaduser = get-azureaduser -objectID $user.UserPrincipalName
$SKUs = $aaduser | Select UserPrincipalName,ImmutableID -ExpandProperty AssignedLicenses
e.g output:
UserPrincipalName ImmutableId DisabledPlans SKUId
----------------- ----------- ------------- -------------
User01#contoso.com x+MVG6EKEUWHi3r6zjgzCA== {041fe683-03e4-45b6-b1af-c0cdc516da4f... 6fd2c87f-b296-42f0-b197-1e91e994b900
User01#contoso.com x+MVG6EKEUWHi3r6zjgzCA== {} 0c266dff-15dd-4b49-8397-2bb16070ed52
Here is my script :
$CSVfile = "C:\temp\LicenseToGroupUsers.csv"
# Import the CSV file
try {
$users = import-csv $CSVfile
}
catch {
$errorZero = $Error[0]
write-host "Error: " $errorZero -ForegroundColor Red #Writes the latest error
Break
}
write-warning "About to add the following users to license groups for complete SKU:"
foreach ($user in $users){
write-host $user.UserPrincipalName
}
Read-Host -Prompt "Press Enter to continue or CTRL+C to quit"
$e3 = -split "0c266dff-15dd-4b49-8397-2bb16070ed52 6fd2c87f-b296-42f0-b197-1e91e994b900"
$e1 = -split "18181a46-0d4e-45cd-891e-60aabd171b4e 0c266dff-15dd-4b49-8397-2bb16070ed52"
$TEAMS_EXPLORATORY = -split "710779e8-3d4a-4c88-adb9-386c958d1fdf 0c266dff-15dd-4b49-8397-2bb16070ed52"
#$FLOW_FREE_E3 = -split "f30db892-07e9-47e9-837c-80727f46fd3d 6fd2c87f-b296-42f0-b197-1e91e994b900 0c266dff-15dd-4b49-8397-2bb16070ed52"
foreach ($user in $users){
$groupsToAdd = #()
$groupsToRemove = #()
write-host "Processing" $user.UserPrincipalName
# Get licensed SKUs for the user
$aaduser = get-azureaduser -objectID $user.UserPrincipalName
#$SKUs = $aaduser | Select UserPrincipalName,ImmutableID -ExpandProperty AssignedLicenses
#Get the AD ObjectGuid for the group add (cannot use UPN)
$ImmutableID = "" #Null these out otherwise gets reused from previous
#Have to match using the guid
$ImmutableID = $aaduser.ImmutableID
if ($ImmutableID) {$objectGUID = ([GUID][System.Convert]::FromBase64String($ImmutableID)).Guid}
else {
write-warning "Error getting ImmutableID for $UPN, user is likely cloud only, skipping"
Break
}
# test 1
$licenses = $aaduser.AssignedLicenses.SkuId
$is_e1 = !($e1 | ForEach-Object { $licenses.Contains($_) }).Contains($false)
if($is_e1 -eq "True"){
try {
write-host "Adding" $user.UserPrincipalName"to E1Group" -ForegroundColor Green
Write-Host "Test 1: $is_e1"
}
catch {
$errorZero = $Error[0]
write-host "Error: " $errorZero -ForegroundColor Red #Writes the latest error
}
}
$is_e3 = !($e3 | ForEach-Object { $licenses.Contains($_) }).Contains($false)
if($is_e3 -eq "True"){
try {
write-host "Adding" $user.UserPrincipalName"to E3Group" -ForegroundColor Green
Write-Host "Test 3: $is_e3"
}
catch {
$errorZero = $Error[0]
write-host "Error: " $errorZero -ForegroundColor Red #Writes the latest error
}
}
$is_TEAMS_EXPLORATORY = !($TEAMS_EXPLORATORY | ForEach-Object { $licenses.Contains($_) }).Contains($false)
if($is_TEAMS_EXPLORATORY -eq "True"){
try {
write-host "Adding" $user.UserPrincipalName"to (TEAMS_EXPLORATORY)E1Group" -ForegroundColor Green
Write-Host "Test 1: $is_TEAMS_EXPLORATORY"
}
catch {
$errorZero = $Error[0]
write-host "Error: " $errorZero -ForegroundColor Red #Writes the latest error
}
}
<# $is_FLOW_FREE_E3 = !($FLOW_FREE_E3 | ForEach-Object { $licenses.Contains($_) }).Contains($false)
if($is_FLOW_FREE_E3 -eq "True"){
try {
write-host "Adding" $user.UserPrincipalName"to (FLOWFREE)E3Group" -ForegroundColor Green
Write-Host "Test 1: $is_FLOW_FREE_E3"
}
catch {
$errorZero = $Error[0]
write-host "Error: " $errorZero -ForegroundColor Red #Writes the latest error
}
}#>
}
To test agains a combination of SkuID's, using a lookup hashtable as in your first approach is not the easiest way I think. Your current approach looks much better to me, only I would not put the ID's in array variables, but test them literally against the ID's as they are found in the users AssignedLicenses.
Something like this:
$CSVfile = "C:\temp\LicenseToGroupUsers.csv"
# Import the CSV file
$users = Import-Csv -Path $CSVfile
Write-Warning "About to add the following users to license groups for complete SKU:"
$users.UserPrincipalName -join [environment]::NewLine
Write-Host
$answer = Read-Host -Prompt "Press Enter to continue or Q to quit"
if ($answer[0] -eq 'Q') { Clear-Host; exit }
foreach ($user in $users) {
Write-Host "Processing" $user.UserPrincipalName
$ImmutableID = $null # Null these out
$ADGroup = $null
# Get licensed SKUs for the user
$aaduser = Get-AzureADUser -objectID $user.UserPrincipalName
# Get the AD ObjectGuid for the group add (cannot use UPN)
# Have to match using the guid
$ImmutableID = $aaduser.ImmutableID
if (![string]::IsNullOrWhiteSpace($ImmutableID)) {
$objectGUID = ([GUID][System.Convert]::FromBase64String($ImmutableID)).Guid}
else {
Write-Warning "Error getting ImmutableID for $($user.UserPrincipalName), user is likely cloud only, skipping"
continue # skip this one and proceed with the next user
}
$licenses = #($aaduser.AssignedLicenses.SkuId) # force it to be an array
##########################################################################################
# Apparently, SkuId '0c266dff-15dd-4b49-8397-2bb16070ed52' is needed for all combinations,
# so we could already rule out users that do not have that ID in their $licenses..
# if that is indeed the case, you can simplify al other tests by not having to check
# for this ID every time..
# for now, this is an assumption, so commented out.
# if (!($licenses -contains '0c266dff-15dd-4b49-8397-2bb16070ed52')) {
# Write-Warning "Could not determine a group for user $($user.UserPrincipalName)"
# continue # skip this one and proceed with the next user
# }
##########################################################################################
# test E1: 'Microsoft 365 Audio Conferencing' and 'OFFICE 365 E1'
if ($licenses -contains '0c266dff-15dd-4b49-8397-2bb16070ed52' -and
$licenses -contains '18181a46-0d4e-45cd-891e-60aabd171b4e') {
# Add this user to group 'O365_E1_Users'
$ADGroup = 'O365_E1_Users'
}
# test E3: 'Microsoft 365 Audio Conferencing' and 'OFFICE 365 E3'
elseif ($licenses -contains '0c266dff-15dd-4b49-8397-2bb16070ed52' -and
$licenses -contains '6fd2c87f-b296-42f0-b197-1e91e994b900') {
if ($licenses -contains 'f30db892-07e9-47e9-837c-80727f46fd3d') { # also 'MICROSOFT FLOW FREE' ?
# Add this user to group 'FLOW_FREE_E3'
$ADGroup = 'FLOW_FREE_E3'
}
else {
# Add this user to group 'O365_E3_Users'
$ADGroup = 'O365_E3_Users'
}
}
# test 'Microsoft 365 Audio Conferencing' and 'MICROSOFT TEAMS EXPLORATORY'
elseif ($licenses -contains '0c266dff-15dd-4b49-8397-2bb16070ed52' -and
$licenses -contains '710779e8-3d4a-4c88-adb9-386c958d1fdf') {
# Add this user to group 'TEAMS_EXPLORATORY'
$ADGroup = 'TEAMS_EXPLORATORY'
}
# finished the conditions, now see if we can add the user to one of the groups
if (![string]::IsNullOrWhiteSpace($ADGroup)) {
try {
Write-Host "Adding $($user.UserPrincipalName) to $ADGroup" -ForegroundColor Green
# Add-ADGroupMember -Identity $ADGroup -Members $objectGUID
}
catch {
Write-Host "Error: $($_.Exception.Message)" -ForegroundColor Red
}
}
else {
Write-Warning "Could not determine a group for user $($user.UserPrincipalName)"
}
}
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.
I'm trying to create a script that creates a unique user I Active Directory.
Before the script can create a user it needs to make sure the username is available by asking two different domains if the generated username exists. The problem is I don't how generate a new username or rerun part of the script if necessary. Lets say that if the generated username exists in one of the domains I wan't to generate a new username and try again with the new one. This is what I have so far.
Import-Module ActiveDirectory
$firstname = "Test"
$lastname = "Tester"
$SecondaryDomain = "server1.domain1.net"
$PrimaryDomain = "server2.domain2.net"
$ErrorActionPreference = "SilentlyContinue"
$Generate = [Char[]]"$firstname$lastname"
$Generatedusername = ($Generate | Get-random -Count 3) -join ""
Write-host $Generatedusername
if (Get-ADUser -Filter {SamAccountName -eq $Generatedusername} -Server $PrimaryDomain)
{
#If the variable equals 1 the user exists
$PrimaryDomainOK = "1"
}
else
{
$PrimaryDomainOK = "0"
}
if (Get-ADUser -Filter {SamAccountName -eq $Generatedusername} -Server $SecondaryDomain)
{
#If the variable equals 1 the user exists
$SecondaryDomainOK = "1"
}
else
{
$SecondaryDomainOK = "0"
}
Write-host "Primary Domain $PrimaryDomainOK"
Write-host "Secondary Domain $SecondaryDomainOK"
If ($PrimaryDomainOK -and $SecondaryDomainOK -eq 0)
{
Write-host "Creating Account"
}
Else
{
Write-host "Can't create account"
}
Add a while statement embracing your generation/verification which checks for the status of $PrimaryDomainOK and $SecondaryDomainOK.
...
$Generate = [Char[]]"$firstname$lastname"
while(($PrimaryDomainOK -ne 0) -and ($SecondaryDomainOK -ne 0)) {
$Generatedusername = ($Generate | Get-random -Count 3) -join ""
Write-host $Generatedusername
...
...
...
If ($PrimaryDomainOK -and $SecondaryDomainOK -eq 0)
{
Write-host "Creating Account"
}
Else
{
Write-host "Can't create account"
}
}
Remember the closing brace.