Creating New Contacts In Bulk AND adding to distros - powershell

I have a script that creates bulk external contacts using PS and a CSV. I would like to tweak this to not only create the contact but also add the contact to any specified distros.
foreach($contact in (import-csv c:\users\ME\desktop\contactstest.csv)){
$properties = #{
type = 'Contact'
name = $contact.firstname + ", " + $contact.lastname
OtherAttributes = #{'mail' = "$($contact.email)"}
Path = "OU=External Contacts,DC=Company,DC=org"
}
New-ADObject #properties
}
If I have the CSV with the following columns. Is this do-able?
Firstname/lastname/email/Group
The CSV is located on the desktop of the DC.
CSV EXAMPLE
first,last,email,group
billy,bob,bb#aol.com,emailist1

I see now what went wrong the first time.
What I meant was for you to replace the line New-ADObject #properties with my code block. Since you left that in aswell, the code tried to create the contact twice, leading to the error message and skipping the part where it add the contact to the group(s).
If your CSV looks like this:
first,last,email,group
billy,bob,bb#aol.com,emailist1
jane,doe,jd#somewhereintheworld.com,emaillist1;emaillist5
Note the second contact is to be added to two groups, separated by a semi-colon ;
Here the code in full:
foreach ($contact in (import-csv c:\users\ME\desktop\contactstest.csv)) {
# create a hash table for splatting the parameters to New-ADObject
$properties = #{
type = 'Contact'
name = $contact.firstname + ", " + $contact.lastname
OtherAttributes = #{'mail' = "$($contact.email)"}
Path = "OU=External Contacts,DC=Company,DC=org"
}
# create the contact and capture the resulting object
$newContact = New-ADObject #properties -PassThru -ErrorAction SilentlyContinue
if ($newContact) {
# if success, add this contact to the group(s)
$contact.Group -split ';' | ForEach-Object {
Add-ADGroupMember -Identity $_ -Members $newContact
}
}
else {
Write-Warning "Could not create contact $($contact.lastname)"
}
}
P.S. You can also include the PassThru and ErrorAction parameters to the splatting hash:
$properties = #{
type = 'Contact'
name = $contact.firstname + ", " + $contact.lastname
OtherAttributes = #{'mail' = "$($contact.email)"}
Path = "OU=External Contacts,DC=Company,DC=org"
PassThru = $true
ErrorAction = 'SilentlyContinue'
}

Related

SMLETS: Powershell

We want to generate an SR per row based on the criteria of a CSV file looking like:
SR templete
The additional criterion:
If the SLO countdown is less than 7 days then the due date is always 7 days for the ticket to be due. Otherwise then then countdown is number SLO _Countdown
The support group is always servicedesk
Unless the host_name does not contain "RES" then it is the support group is EITS_HW_Notes and it will be assigned to "custodian".
No matter what an SR is generated even if null.
My difficulty is my lack familiarity with smlets. I am happy to consider generating tickets via email as well. But would like help on how best to do that via powershell. But the code I came up with is below:
`#Prod
#$GLOBAL:smdefaultcomputer = "prodserver"
#Test
$GLOBAL:smdefaultcomputer = "testserver"
Import-Module SMlets
$path = "C:\Temp\Test.csv"
$csv = Import-csv -path $path
#Variable / Class Setup
$srClass = Get-SCSMClass -name System.WorkItem.ServiceRequest
$srprior = Get-SCSMEnumeration -Name ServiceRequestPriorityEnum.Medium
$srurg = Get-SCSMEnumeration -Name ServiceRequestUrgencyEnum.Medium
#$ararea = get-SCSMEnumeration -Name ServiceRequestAreaEnum.Other
$ararea = get-SCSMEnumeration -Name Enum.add3768303064ec18890170ba33cffda
$title = “Title Goes Here”
$descrip = "Description info goes here"
#Service Request Arguements
$srargs = #{
Title = $title;
Urgency = $srurg;
Priority = $srprior;
ID = “SR{0}”;
Area = $ararea;
SupportGroup = "ServiceDesk";
Description = $descrip
}
#Create Service Request
$newServiceRequest = New-SCSMOBject -Class $srClass -PropertyHashtable $srargs -PassThru
#get SR ID of the new object
$SRId = $newServiceRequest.id
#Get Projection & Object for Created Service Request
$srTypeProjection = Get-SCSMTypeProjection -name System.WorkItem.ServiceRequestProjection$
$SRProj = Get-scsmobjectprojection -ProjectionName $srTypeProjection.Name -filter “Id -eq $SRId”
#Set Afffected User
$userClass = Get-SCSMClass -Name Microsoft.AD.UserBase$
$cType = "Microsoft.EnterpriseManagement.Common.EnterpriseManagementObjectCriteria"
$cString = "UserName = 'itservicenotifications' and Domain = 'SHERMAN'"
$crit = new-object $cType $cString,$userClass
$user = Get-SCSMObject -criteria $crit
$AffectedUserRel = get-scsmrelationshipclass -name System.WorkItemAffectedUser$
New-SCSMRelationshipObject -RelationShip $AffectedUserRel -Source $newServiceRequest -Target $user -Bulk`
I tried the above code but am running into issues recognizing the column name in the CSV file and am unfamiliar with SMLETS + powershell if statements.
Columns are:
CSV Columns
CSV text with examples is: Columns with examples
Could you paste the CSV columns as text, please? Or, better, a sample CSV with one or two rows (redact any sensitive data).
I would expect a CSV to contain multiple rows - even if yours does not, it's good defensive programming to act as if it does. So the first modification I suggest is:
$path = "C:\Temp\Test.csv"
$csv = Import-csv -path $path
foreach ($Row in $csv)
{
# the rest of your code goes in here
}
I find it helpful while debugging to go step-by-step. If I understand your problem right, it's about building the right hashtable in $srargs to pass to New-SCSMOBject. So the next modification is:
foreach ($Row in $csv)
{
$srClass = Get-SCSMClass -name System.WorkItem.ServiceRequest
# etc
$srargs = #{
Title = $title
Urgency = $srurg
Priority = $srprior
ID = “SR{0}”
Area = $ararea
SupportGroup = "ServiceDesk"
Description = $descrip
}
$srargs # write the hashtable so you can inspect it
# skip the rest of the code for now
}
I understand your question as "how to express the logic of":
support group is always servicedesk
Unless the host_name does not contain "RES"
then the support group is contents of EITS_HW_Notes cell in CSV
and it will be assigned to "custodian"
I can't help you with setting the assignee. But we can rejig the rest of the statement:
if host_name contains "RES"
SupportGroup = servicedesk
else
SupportGroup = contents of EITS_HW_Notes cell
You can code that like this:
foreach ($Row in $csv)
{
$srClass = Get-SCSMClass -name System.WorkItem.ServiceRequest
# etc
if ($Row.host_name -like "*RES*")
{
$SupportGroup = "ServiceDesk"
}
else
{
$SupportGroup = $Row.EITS_HW_Notes
}
$srargs = #{
Title = $title
# etc
SupportGroup = $SupportGroup
Description = $descrip
}
}
Does that get you any closer to your solution?

Can someone help me with a powershell script that will add users to groups based Their OU?

I am trying to create a script to add people to groups and i would like something like an IF statement that will automatically add our employees to their VLAN group if they are in a certain OU. Lets say our employees are in OU = test and they will be added to the group "Test VLAN"
I would like to add this to my script that checks which OU they're in and adds them to a specific VLAN group.
Thanks
Here you go, no activedirectory module needed.
You would need to adjust the LDAP filter to your liking. Currently it does it for all user objects with the Title field populated
$VerbosePreference = "Continue"
[void][System.Reflection.Assembly]::LoadWithPartialName("System.DirectoryServices.AccountManagement")
# LDAP search filter, this gets all users with the title field populated
$searchFilter = "(&(objectclass=user)(title=*))"
# Hash mapping between group name and OU
$mapping = #(
[PSCustomObject]#{ Name = "Test VLAN"; Value = "OU=Test,OU=Users,DC=contoso,DC=com"}
[PSCustomObject]#{ Name = "Test VLAN 2"; Value = "OU=Test2,OU=Users,DC=contoso,DC=com"}
[PSCustomObject]#{ Name = "Test VLAN 123123"; Value = "OU=Test123123,OU=Users,DC=contoso,DC=com"}
)
# Get all users in Active Directory
$directorySearcher = New-Object System.DirectoryServices.DirectorySearcher([ADSI]"")
$directorySearcher.Filter = $searchFilter
$directorySearcher.PropertiesToLoad.Add("samaccountname")
$directorySearcher.PropertiesToLoad.Add("distinguishedname")
$users = $directorySearcher.FindAll()
$domainName = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain().Name
$principalContext = [System.DirectoryServices.AccountManagement.PrincipalContext]::new("Domain",$domainName)
# Loop through users and add them to group
foreach ($user in $users) {
$userPrincipal = [System.DirectoryServices.AccountManagement.UserPrincipal]::FindByIdentity($principalContext, $user.Properties.samaccountname)
if ($userPrincipal) {
$vlanGroup = $mapping.Where({$user.Properties.distinguishedname.EndsWith($_.Value)})
if ($vlanGroup) {
$groupPrincipal = [System.DirectoryServices.AccountManagement.GroupPrincipal]::FindByIdentity($principalContext, $vlanGroup.Name)
if ($userPrincipal.IsMemberOf($groupPrincipal)) {
Write-Verbose "User '$($user.Properties.samaccountname)' is already memberof '$($vlanGroup)'"
}
else {
$groupPrincipal.Members.Add($userPrincipal)
$groupPrincipal.Save()
Write-Verbose "Added user '$($user.Properties.samaccountname)' to group '$($vlanGroup)'"
}
}
else {
Write-Verbose "No VLAN mapping found for user '$($user.Properties.samaccountname)'"
}
}
else {
Write-Verbose "Unable to find userprincipal for '$($user.Properties.samaccountname)'"
}
Clear-Variable 'userPrincipal', 'vlanGroup', 'groupPrincipal' -ErrorAction SilentlyContinue
}

AD user creation with SamAccountName availability check loop

I'm learning Powershell by making a script that will hopefully automate everything that needs to be done when we get a new hire or a consultant. Currently I'm working on the part that will create the AD account. Below are the variables specific to this portion of the script.
#Variables for preliminary SamAccountName, modifiers and initials array
$PrelSamAccountName = ("se" + [string]$GivenName.Substring(0,3) + [string]$SurName.Substring(0,3)).ToLower()
$Modifier1 = 1
$Modifier2 = 0
$InitialsArray = #("x","y","z")
Here is the loop. I cut out a bunch of parameters on New-ADUser to make it less cluttered.
try {
#Checks if preliminary SamAccountName is taken or not
$ADCheck = Get-ADUser -Filter {SamAccountName -eq $PrelSamAccountName}
#Creates new user
New-ADUser -Name $Name -SamAccountName $PrelSamAccountName
} catch {
#Replaces final character in preliminary SamAccountName with "1++"
while (($ADCheck | Select-Object -ExpandProperty SamAccountName) -eq $PrelSamAccountName) {
$PrelSamAccountName = ([string]$PrelSamAccountName.Substring(0,7) + ($Modifier1++)).ToLower()
}
#Changes $Initials from $null to x/y/z if an existing user has identical name as new user
while (($ADCheck | Select-Object -ExpandProperty Name) -eq $Name) {
$Initials = $InitialsArray[$Modifier2++]
$Name = $GivenName + " " + $Initials + " " + $SurName
}
}
Everything is working as intended, except for the fact that a new user is created every other time I run the loop. Ideally I would want it to create a new user every time it is run. :)
I'm assuming it has something to do with the placement of the $ADCheck variable, but after having rewritten this portion multiple times I simply can't get it to work. Any ideas?
Thanks in advance.
You have some logical problems in here:
Follow this Approach:
Pseudocode:
while (user exist) {create new username}
new-aduser new username
PS Code:
function new-sam
{
#creates new sam
}
$sam = "username"
$a=$false
while (!$a)
{
try {
$a = [bool](Get-ADUser -Identity $sam )
}
catch {
$a = $false; $sam = new-sam
}
}
new-aduser ....

Powershell [Ref] Value not updating main object

I'm running Powershell V2 on XP:
This is part of a larger script and I'm noticing some anomolies with the way I'm able to use reference objects to update the "master" object. This started off as a way to avoid typing out the complicated name of the property each time - I could easily expand out to the full name but now the reason for this behaviour is seriously bugging me.
[CmdletBinding()]
Param()
Function Breakhere {Write-Verbose " "}
Set-PSBreakpoint -Command Breakhere
$Data = #"
UserID
MyUser1
MyUser2
User3
"#
$Data = $Data|ConvertFrom-Csv
$Domains = "DomainA","DomainB"
$Props = "ParentContainer","AccountIsDisabled","employeeNumber"
$Connection = New-Object HashTable
ForEach ($Domain in $Domains)
{
Write-Verbose "Current Domain: $Domain"
# Add necessary headers to main data
$text1 = "$($Domain)_ADObject"
$text2 = "$($Domain)_Confidence"
$Data = $Data |Select *,$text1
$Data = $Data |Select *,$text2
#Bind to each domain and save the connection contexts into a hashtable
Write-Verbose "Binding to $Domain"
$Connection.Add($Domain,(Connect-QADService -service $Domain))
}
ForEach ($User in $Data)
{
ForEach ($Domain in $Domains)
{
$User."$($Domain)_ADObject" = Get-QADUser -Connection $Connection[$Domain] -SamAccountName $User.UserID -DontUseDefaultIncludedProperties -IncludedProperties $Props|Select $Props
# Referencing the confidence parameter does not seem to work.
$CF = [ref]$User."$($Domain)_Confidence"
# Weirdly, this one does work.
$AD = [ref]$User."$($Domain)_ADObject"
If ($AD.Value)
{
$CF.Value = 1
Breakhere # Break here and allow for opportunity to inspect $user and $CF objects
If ($AD.Value.AccountIsDisabled)
{
Write-Verbose "$Domain\$($User.UserID): Account Disabled"
$CF.Value *= 0.8
}
}
Else
{
Write-Verbose "$Domain\$($User.UserID): No AD Object found"
$CF.Value = 0
}
}
} #End ForEach $UserID
At the breakpoint, if I query $User, I receive something similar to the following:
UserID : MyUser1
DomainA_ADObject : #{ParentContainer=DomainA/Users; AccountIsDisabled=False; employeeNumber=123456}
DomainA_Confidence :
DomainB_ADObject :
DomainB_Confidence :
All good. Should I wish, I can even use the $AD ref object and update DomainA_ADobject:
$AD.Value.employeeNumber = 9999
$User
UserID : MyUser1
DomainA_ADObject : #{ParentContainer=DomainA/Users; AccountIsDisabled=False; employeeNumber=9999}
DomainA_Confidence :
DomainB_ADObject :
DomainB_Confidence :
However, try this with the $CF ref and the same thing doesn't happen
$CF.Value = 2
$CF
Value
-----
2
$User
UserID : MyUser1
DomainA_ADObject : #{ParentContainer=DomainA/Users; AccountIsDisabled=False; employeeNumber=9999}
DomainA_Confidence : *<====== Expecting this to update!*
DomainB_ADObject :
DomainB_Confidence :
Why the difference? Is there any way to query a [ref] object and see what it's pointing to? I can't see why one of these is working and the other isn't. They both seem to be set up in the same way. Tried this in ISE and console, same behaviour in both.
My guess is, that this is caused by dots in the name of domains.
Reducing this to basics $CF.Value = 1 does someting like
$Data[0].domain.name.net.au_Confidence.value = 1
This will not work. This will:
$Data[0]."domain.name.net.au_Confidence".value = 1
Perhaps this will fix it?:
$CF = [ref]$User."`"$($Domain)_Confidence`""

String comparison from XML failing

I am currently working on a project in PowerShell. The project downloads the NVD database XML, loops through a separate CSV for scan results from Nexpose, and pulls CVSS scores for each vulnerability identified with a CVE number.
It seems that matching the CVE number from the sheet (string) with the CVE number in the XML (also a string) is failing completely. The code i am using is below:
clear
[xml]$nvdxml = (New-Object system.Net.WebClient).DownloadString("http://static.nvd.nist.gov/feeds/xml/cve/nvdcve-2.0-recent.xml")
$nsmgr = New-Object System.XML.XmlNamespaceManager($nvdxml.NameTable)
$nsmgr.AddNamespace('xsi','http://www.w3.org/2001/XMLSchema-instance')
$nsmgr.AddNamespace('vuln','http://scap.nist.gov/schema/vulnerability/0.4')
$nsmgr.AddNamespace('cvss','http://static.nvd.nist.gov/feeds/xml/cve/nvdcve-2.0-recent.xml')
$nsmgr.AddNamespace('df','http://scap.nist.gov/schema/feed/vulnerability/2.0')
$nvdxml.SelectNodes('//vuln:product',$nsmgr) | out-null
$nvdxml.SelectNodes('//vuln:vulnerable-configuration',$nsmgr) | out-null
$nvdxml.SelectNodes('//vuln:vulnerable-software-list',$nsmgr) | out-null
$nvdxml.SelectNodes('//default:nvd',$nsmgr) | out-null
$nvdxml.SelectNodes('//default:entry',$nsmgr) | out-null
$x = import-csv "test-report.csv"
$items = #()
$x | where {$_."Vulnerability Test Result Code" -like "v*"} | %{
$item = #{}
$vid = $_."Vulnerability CVE IDs"
$entry = ""
$item["Vname"] = $_."Vulnerability Title"
$item["VNode"] = $_."Asset IP Address"
$item['VID'] = $vid
$entry = $nvdxml.nvd.entry | where { $_."cve-id" -eq $vid }
$item['Score'] = $entry.cvss.base_metrics.score
$items += $item
}
$items
The $items array contains a vulnerability which has a CVE ID, but the string comparison is utterly failing. When I query for the object's type I get
You cannot call a method on a null-valued expression.
At line:25 char:19
+ $entry.GetType <<<< ()
+ CategoryInfo : InvalidOperation: (GetType:String) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
When I assign a CVE ID to a string, and attempt to get the relevant vulnerability from the XML for that string, the comparison returns no results; yet, when I replace the variable with the quoted string of the same ID, the query returns the correct result. So, this would fail
$cveID = "CVE-2003-1567"
$nvdxml.nvd.entry | where { $_."cve-id" -eq $cveID }
However, this works fine
$nvdxml.nvd.entry | where { $_."cve-id" -eq "CVE-2003-1567" }
Any ideas? I have tried explicitly casting both $_."cve-id" and $cveID as String with the same results.
I would put all the entries in a hashtable, then look it up via the CVE ID:
$entriesByID = #{ }
$nvdxml.nvd.entry |
ForEach-Object { $entriesByID[$_.id] = $_ }
Note that instead of using the cve-id element, I'm using the id attribute on the event element.
Then, you can look it each entry in the hashtable:
$entry = $entriesByID[$vid]
If you're married to your original approach, you may be running into namespace issues. I would try using SelectNodes instead of PowerShell's virtual XML object properties:
$entry = $nvdxml.SelectSingleNode('/nvd/entry[vuln:cve-id = "$vid"]')
# or
$entry = $nvdxml.SelectSingleNode('/nvd/entry[#id = "$vid"]')