Why are my values repeating when information is being inserted? - sql-server

I'm creating a script that goes through the Active directory and then searches for information based on another query when the information is entered and there is no information from the active directory the information from the last entered area enters into that SQL row even if there is no information for that site. Should I create an if else statement within one of the foreach loop to fix this problem?
while($rows.Read()){
$SITEINFO = $rows['ActiveDirectoryOU']
$ADARRAY= Get-ADGroupMember -Identity $SITEINFO | Get-ADUser -Properties ('Mail')
ForEach($OBJECT in $ADARRAY){
$NAME = $OBJECT.Name
$USER = $OBJECT.SamAccountName
$EMAIL = $OBJECT.Mail
$SITECODE = $SITEINFO
$INSERT = "INSERT INTO $TABLE VALUES ('$USER','$SITECODE','$EMAIL', '$NAME');"
$SQL = $SQLCON.CreateCommand()
$SQL.CommandText = $INSERT
$SQL.ExecuteNonQuery()
}
}
}
$SQLCONN.Close()

Related

How do i use PSGSUITE PS module to add google groups?

Ok so I work for a small business, and they use a google spread sheet as the "Phone list"
for finding and contacting employees. I installed the PSGSUITE powershell module and it seems to work very well, but im new to powershell and coding in general. The filter sheet i made along with the phone list places the employees in there respective groups. Example then The code.
"phone list"
Name # Company code Ext. Department Job Title Email
Hayden 111-222-333 JOP IT Technician example#example.com
"filter 2sheet"
JOP SPD
hayden#.com lisa#.com
john#.com arron#.com
david#.com mike#.com
I want to add these emails to there respective google groups
## NOVA BEAZ ##
## add groups in google based on company title
###
####
# Import Modules
Import-Module PSGSuite
# Create Array of Groups
$Title = (Import-GSSheet -SpreadsheetId "1NtCT5ruoL4Kf4-ec55xe-L8esXcSY8orfd-zOFK4q4k" -SheetName "Filter" -Headers "None" -Range "A1:1")
$Title = $Title | % { $_ }
$Groups = (Get-GSgroup -Fields "Name" )
if($Title = $Groups)
#{add that users email to the group}
#else
{echo "there is now group that matches that"}
The main issue is I really just dont know how to correctly run through the arrays and select all the emails in that row to add to the google groups, I think I need a array or object list form of storing my emails, I want this to be dynamic.
Excerpts from my blog post on how to use the PSGusite module.
Please check if the following answers your question. If not, let me know.
User Process
To begin, we need to import the module and then use the command Get-GSDriveFileList to find the Google Sheet where our data is stored.
Next, we use the command Import-GSSheet to import our user and group data.
Get Data from GSheet
# Import module
Import-Module -Name PSGSuite
# Discover spreadsheet Id in drive file list
$Spreadsheet = Get-GSDriveFileList -Filter "name = 'UserManagement'"
# Get data from each sheet from Google spreadsheet
$UserData = Import-GSSheet -SpreadsheetId $Spreadsheet.Id -SheetName 'Users'
$GroupData = Import-GSSheet -SpreadsheetId $Spreadsheet.Id -SheetName 'Groups'
Create Organization Units
We use Get-GSOrganizationalUnit to determine if the OU exists. And then we use New-GSOrganizationalUnit to create it if it does not.
foreach ($Group in $GroupData) {
$SplitPath = $Group.OrgUnitPath -Split '/'
$ParentPath = $SplitPath[0..($SplitPath.Count -2)] -join '/'
$OUPath = $SplitPath[-1]
$OrgUnit = Get-GSOrganizationalUnit -SearchBase $Group.OrgUnitPath -SearchScope Base -ErrorAction SilentlyContinue
if ($OrgUnit) {
"Org Unit {0} exists at {1}" -f $OrgUnit.OrgUnitPath,$OrgUnit.ParentOrgUnitPath
} else {
"Org Unit {0} does not exist; attempting to create in {1}" -f $Group.OrgUnitPath,$ParentPath
try {
$GSOrgUnit = New-GSOrganizationalUnit -Name $OUPath.ToLower() -ParentOrgUnitPath $ParentPath -Description $Group.Description
"Created {0} : {1}" -f $GSOrgUnit.OrgUnitPath,$GSOrgUnit.Description
}
catch {
"Unable to create {0}" -f $Group.OrgUnitPath
}
}
}
Create Groups
Using the command Get-GSGroup, we check if the group exists. If the group does not already exist, use New-GSGroup to create the group from the spreadsheet.
foreach ($Group in $GroupData) {
$GSGroup = Get-GSGroup -Group $Group.Name -ErrorAction SilentlyContinue
if ($GSGroup) {
"Group {0} exists" -f $Group.Name
} else {
"Group {0} does not exist; attempting to create" -f $Group.Name
try {
$NewGSGroup = New-GSGroup -Name $Group.Name -Email $Group.Email -Description $Group.Description
"Created {0} : {1}" -f $NewGSGroup.Name,$NewGSGroup.Description
}
catch {
"Unable to create {0}" -f $Group.Name
}
}
}
Create Users
Create the users listed in the spreadsheet.
First, determine the department based on the user type.
Using the department, set the variable for the org unit path.
Create the required hashtable for CustomSchemas to add the EmployeeType to the user.
Generate a random secure password.
Using the command New-GSUser, create the new user.
If the user is successfully created, use the command New-GSUserAlias for best effort to create an email alias based on the user’s full name.
foreach ($User in $UserData) {
$Domain = $User.Email.Split('#')[1]
switch ($User.UserType) {
'Faculty' { $Department = 'Academics'}
'Staff' { $Department = 'Business' }
}
# Set OU path
$OrgUnitPath = $GroupData.Where({$_.Name -eq $Department}).OrgUnitPath
# Set employee type custom schema
$CustomSchemas = #{
CustomUniversity = #{
EmployeeType = $User.UserType
}
}
# Set a random secure string
$Password = ConvertTo-SecureString -String (Get-RandomPassword) -AsPlainText -Force
$NewGSUserParams = #{
PrimaryEmail = $User.Email
FullName = $User.FullName
GivenName = $User.GivenName
FamilyName = $User.FamilyName
OrgUnitPath = $OrgUnitPath
CustomSchemas = $CustomSchemas
Password = $Password
}
$NewUser = New-GSUser #NewGSUserParams -ErrorAction SilentlyContinue
if ($NewUser) {
'Created user {0} with primary email {1}' -f $User.FullName,$User.Email
} else {
'Failed to create user {0}' -f $User.Email
}
New-GSUserAlias -User $NewUser.PrimaryEmail -Alias ( $NewUser.Name.FullName.Replace(' ',''),$Domain -join '#') -ErrorAction SilentlyContinue | Out-Null
}
The Get-RandomPassword function is a mock-up. You would need to provide your own password method.
You can omit CustomSchemas from the hashtable. The blog post shows how to manually create new attributes, if you are interested.
Assign Users to Groups
Next, we use Get-GSUserList to get a list of all users in the parent OU, and then add the user to the group with Add-GSGroupMember.
$UserToGroupList = Get-GSUserList -SearchBase '/test' -SearchScope Subtree
foreach ($User in $UserToGroupList) {
switch -regex ($User.OrgUnitPath) {
'academics' { $GroupName = 'Academics'}
'business' { $GroupName = 'Business'}
}
try {
Add-GSGroupMember -Identity $GroupName -Member $User.User -ErrorAction Stop | Out-Null
'Added {0} to group {1}' -f $User.User,$GroupName
}
catch {
'Failed to add {0} to group {1}' -f $User.User,$GroupName
}
}
Note: I manually created a /test organizational unit and blocked automatic assignment of a license since I’m using my personal G Suite account. I don’t want any surprises at the end of the month.

Looping through pscustomobject dataset and add to a SQL DB

I have a variable, $CiscoCMDB, which holds data for 2968 records like this (some fields are all filled, some are not like below):
SearchCode: D12345678911
Name1: 1212
Category: Office Phone
AssetTag:
Status: Stock
SerialNumber: FCH1549BBBB
Model: CISCO IP PHONE 7945G
MacAddress:
Location: SF
OwnerOrganization:
OwnerPerson:
I also have a SQL DB created, CiscoCMDB, with the same name for the columns that are present for the rows. I would like All records to be written to the SQL table. I've tried to use the Write-Datatable cmdlet, but it gave me errors. along with other CMDlets. I figured, if I am using SQL SELECT statements in my code, I should just use SQL INSERTs in my code. Only problem is, I'm not sure how to go about this other than knowing I will need a foreach loop to loop through.
Can someone help me on this as I'm getting confused and frustrated. I'm not exactly sure how to go about looping through correctly.
Pull data from another SQL DB
Organize data furthur (picking the columns I want)
Manipulate data to strip ^C from searchcode (all our search codes are prefaced with C)
Manipulate data to strip ^DN from Name1 field (again all Name1s are prefaced with DN)
Fill in my data
$CMDBCiscoQuery = #()
foreach ($row in $table) {
$hash = [ordered]#{
'SearchCode' = $row.SearchCode
'Category' = $row.Category
'Status' = $row.Status
'Name1' = $row.Name1
'SerialNumber' = $row.SerialNumber
'Model' = $row.Model
'MacAddress' = $row.MacAddress
'Location' = $row.Location
'OwnerOrg' = $row.OwnerOrganization
'Owner' = $row.OwnerPerson
}
$obj = New-Object -TypeName PSObject -Property $hash
$CMDBCiscoQuery+= $obj
$CMDBCisco = $CMDBCiscoQuery |
Select #{n='SearchCode';e={$_.Searchcode -replace "^C"}},
#{n='Name1';e={$_.DN -replace "^DN"}}, Category, AssetTag,
Status, SerialNumber, Model, MacAddress, Location,
OwnerOrganization, OwnerPerson
#Filling CMDB table
Write-Verbose "Filling CMDBCisco Table"
$ConnectionTimeout = 30
$ServerInstance = "myserver"
$Database = "Audits"
$conn = New-Object System.Data.SqlClient.SQLConnection
$ConnectionString = "Server={0};Database={1};Connect Timeout={2};Integrated Security = True;" -f $ServerInstance,$Database,$ConnectionTimeout
$conn.ConnectionString = $ConnectionString
$conn.Open()
$cmd = New-Object System.Data.SqlClient.SqlCommand($conn);
$conn.Close()

PowerShell - need ini file for consecutive numbering

I'm currently working on a script for automation. This script should have a global count variable that does not reset itself when the script is executed again. Therefore, I need a configuration file that stores this count variable and uses it when it is called up again. This counting variable is also dependent on an ID. There is therefore a count variable for each ID. The configuration file can be in XML or INI format. Can someone tell me how to create such a file the easiest way and how to add IDs or get the count variable? I dont think "csv-import/export" is the right way.
I've already tried this...
$results = #()
$details = #{
Key1 = $ID
Key2 = $count
Key3 = "sth"
Key4 = "sth"
Key5 = "sth"
}
$results += New-Object PSObject -Property $details
$results | export-csv -Path C:\Users\...\configure.txt -NoTypeInformation
Unfortunately, I can't get any further here, because it overwrites the previous entry every time the ID changes and I don't know how to add additional entries (if the ID already exists), update entries (count variable) and call this count variable to use it in Powershell.
Anybody got a suggestion?
Best Regards
You can use a hash table, Export-CliXml and Import-CliXml to save and load you ID counts to a XML file:
$xmlFilePath = 'idCounts.xml'
# If the XML file exists, it is loaded
if( Test-Path -Path $xmlFilePath -PathType Leaf )
{
$hashTable = Import-Clixml -Path $xmlFilePath
}
# Else a new hash table is initialized
else
{
$hashTable = #{}
}
# Set the count of ID '001' to 1
$hashTable['001'] = 1
# Increment the count of ID '002'
$hashTable['002'] += 1
# Save the hash table to the XML file
$hashTable | Export-Clixml -Path $xmlFilePath
Thank you for all the tips. In the end, I managed it myself in the following way:
if(!((import-csv "C:\Users\...\Desktop\ini.txt") | where-object {$_.Key1 -eq $ID}))
{
$results = #()
$details = #{
Key 1 = $ID
Key 2 = 1
Key 3 = "something"
Key 4 = "something"
Key 5 = "something"
Key 6 = "something"
}
$results += New-Object PSObject -Property $details
$results | export-csv -Path C:\Users\...\Desktop\ini.txt -append -NoTypeInformation
}
The system first checks whether there is an entry with the corresponding ID. If not, an object is created that has that ID. The count variable is set to 1 when it is newly created. The entry is attached to the file with "Export CSV".
$select = (import-csv "C:\Users\...\Desktop\ini.txt" | where{$_.Key1 -eq $ID})
[int]$global:number = [convert]::ToInt32($select.Key2)
To use the count variable, the configuration file is imported. I have set it to "global" because it has to operate over several functions.
($csv = Import-Csv "C:\Users\...\Desktop\ini.txt") | ForEach {
if ($_.Key1 -eq $ID) {
$_.Key2 = $global:number}
}
$csv | Export-Csv "C:\Users\...\Desktop\ini.txt" -NoTypeInformation
At the end, the count variable is updated and transferred back to the file with "Export CSV".
Nevertheless thank you for all the interesting suggestions!
Best Regards

What is the equivalent of the 'GO' statement when using SMO with Powershell?

This is a snippet from my powershell module which creates a login and user in a database:
# code omitted which sets default db, language, etc.
$login.Create()
$user = New-Object ("Microsoft.SqlServer.Management.Smo.User") $db, $username
$user.Login = $login.Name
$user.Create()
An exception is being thrown on the call to $user.Create() with the following message:
Windows NT user or group '[domain\user]' not found. Check the name again.
However $login.Create() executes without a problem and I can see in SSMS that the login exists.
The only thing that comes to mind why this error is being thrown is if I was doing this through T-SQL I'd write the following:
CREATE USER [domain\user] FROM WINDOWS;
GO
CREATE USER [user] FOR [domain\user];
GO
Question regarding issuing a GO statement after CREATE LOGIN
How is this achieved in PowerShell? Or am I doing this wrong?
If $DB is just a string holding a database name change that to be
$db = $server.Databases['YourDatabaseNameHere']
You might also try changing the $user creation line to
$user = New-Object ("Microsoft.SqlServer.Management.Smo.User") $db, $login.Name
$user.Create()
That last shouldn't be necessary but it's what I use that works. If that still doesn't work you might want to post more of your code.
I'm not sure what I changed, but the script seems to be working now. I'll post the full script anyway, and maybe someone can tell me. -shrug-
Function Add-DatabaseUser
{
Param(
[Parameter(Mandatory=$true, HelpMessage="The domain account to create the account for.", Position = 0)]
[string] $loginname
,[Parameter(Mandatory=$true, HelpMessage="The user name for the new account.", Position = 1)]
[string] $username
,[Parameter(HelpMessage="Make this user an database administrator?")]
[switch] $dbadmin
)
$srv = Get-Server
$db = Get-Database
$login = $null;
$user = $null;
Try
{
$login = New-Object ("Microsoft.SqlServer.Management.Smo.Login") $srv, $loginname
$login.DefaultDatabase = "MyDatabase"
$login.Language = "British English"
$login.LoginType = "WindowsUser"
$login.Create()
}
Catch
{
$exception = $_.Exception
while($exception.InnerException -ne $null)
{
$exception = $exception.InnerException
}
Write-Error $exception.Message
return
}
Try
{
$user = New-Object ("Microsoft.SqlServer.Management.Smo.User") $db, $username
$user.Login = $login.Name
$user.Create()
if ($administrator)
{
$user.AddToRole("db_datareader")
$user.AddToRole("db_datawriter")
$user.Alter()
}
}
Catch
{
$exception = $_.Exception
while($exception.InnerException -ne $null)
{
$exception = $exception.InnerException
}
Write-Error $exception.Message
return
}
Write-Host "User was successfully created."
return
}

Fine-tuning Powershell SQL Script

My company has a program that tracks our Employee workouts. When we had this program made, we did not think about adding the ability to Add or Remove an employee to the program.
I wrote a script in PowerShell that allows us to do this easier than in SSMS. I would like to see if anyone can help me clean it up a bit and fine tune it.
My biggest headache is this 1 or -1 that gets returned anytime we execute a function. I would also like this to ask if they are finished, then loop back or exit. Right now it just exits as soon as they are done.
<#Writes the invoker to log#>
$trandate = Get-Date
$tranuser = $env:UserName
<# Variables to open the connection to the SQL server #>
$sqlcn = New-Object System.Data.SqlClient.SqlConnection
$sqlcn.ConnectionString = "server=10.10.1.19\VTSWORKOUT;Integrated
Security=true;Database=VTSWORKOUT;"
<# Read what the user wants to do #>
$input = Read-Host "Do you want to [A]dd a New Employee, [R]emove an Employee or [E]xit?"
switch($input){
<# Stuff for adding an employee to the database #>
A{
$eid = Read-Host "What is the Employees ID number?"
$fname = Read-Host "What is the Employees first name?"
$lname = Read-Host "What is the Employees last name?"
$dept = Read-Host "What department is the Employee in?"
$pay = Read-Host "Is the Employee Salaried? [0]Yes or [1]No"
$hire = Read-Host "When was the Employee hired? Input as MM-DD-YYYY"
Out-File -FilePath "L:\Personnel\WorkoutApp\workouts.log" -Append -InputObject "On $trandate, $tranuser added Employee# $eid, $fname $lname"
$sqlcn.Open()
$sqlcmd = $sqlcn.CreateCommand()
$query = "INSERT INTO employees values (#eid,#lname,#fname,#dept,#pay,#hire)"
$sqlcmd.CommandText = $query
$sqlcmd.Parameters.AddWithValue("#eid", $eid) | Out-Null
$sqlcmd.Parameters.AddWithValue("#fname", $fname) | Out-Null
$sqlcmd.Parameters.AddWithValue("#lname", $lname) | Out-Null
$sqlcmd.Parameters.AddWithValue("#dept", $dept) | Out-Null
$sqlcmd.Parameters.AddWithValue("#pay", $pay) | Out-Null
$sqlcmd.Parameters.AddWithValue("#hire", $hire) | Out-Null
$sqlcmd.ExecuteNonQuery()
$sqlcn.Close()
}
<# Stuff for removing an employee from the database#>
R{
<#Collect reason for removal#>
$reason = Read-Host -Prompt "Why are you deleting this employee?"
$eid = Read-Host "What is the ID number of the Employee you want to remove?"
$sqlcn.Open()
$sqlcmd = $sqlcn.CreateCommand()
$query = "SELECT EmployeeID,FirstName, LastName from Employees WHERE EmployeeID = #eid"
$sqlcmd.CommandText = $query
$sqlcmd.Parameters.AddWithValue("#eid", $eid) | Out-Null
$sqlcmd.ExecuteNonQuery()
$Reader = $sqlcmd.ExecuteReader()
$arry = #()
while ($Reader.Read()) {
$row = #{}
for ($i = 0; $i -lt $reader.FieldCount; $i++)
{
$row[$reader.GetName($i)] = $reader.GetValue($i)
}
#convert hashtable into an array of PSObjects
$arry+= new-object psobject -property $row
}
$sqlcn.Close()
write-host $arry
$empResult = Read-Host "Is that the correct employee? [Y]es or [N]o"
<#If the correct employee was found, continue below.
If the wrong employee was returned, Kill Program #>
switch($empResult) {
Y{
Out-File -FilePath "L:\Personnel\WorkoutApp\workouts.log" -Append -InputObject "On $trandate, $tranuser deleted Employee $eid for the following reason: $reason"
$sqlcn.Open()
$sqlcmd = $sqlcn.CreateCommand()
$query = "DELETE FROM Employees WHERE EmployeeID = #eid"
$sqlcmd.CommandText = $query
$sqlcmd.Parameters.AddWithValue("#eid", $eid)
$sqlcmd.ExecuteNonQuery()
$adp = New-Object System.Data.SqlClient.SqlDataAdapter $sqlcmd
$data = New-Object System.Data.DataSet
$adp.fill($data) | Out-Null
$sqlcn.Close()
}
N{
Out-File -FilePath "L:\Personnel\WorkoutApp\workouts.log" -Append -InputObject "On $trandate, $tranuser tried to deleted Employee $eid. But exited the program before doing so."
Write-Host "Please restart the program. If the issue persists, please contact the IT department."
Read-Host -Prompt "Press Enter to exit"
}
}
}
<# Line to exit the program #>
E{
exit
}
}
Any thoughts on cleaning this up would be greatly appreciated.
This is off-topic, but I'll give you an answer.
Generally, you don't want to use Parameters.AddWithValue() at all, because that sends every parameter as an NVARCHAR. It's not deprecated, but it's not a good idea to use it. If you've got datetimes or other non-string parameters, you can end up with problems. It's usually preferable to use Parameters.Add():
$sqlcmd.Parameters.Add("#eid", [System.Data.SqlDbType]::Int).Value = $eid
Obviously, the datatype you use from [System.Data.SqlDbType] should match the datatype of the actual column in the database. This also has the benefit that there won't be any return value that you need to send to Out-Null or cast as [void].
This is also a mess:
$sqlcmd.ExecuteNonQuery()
$Reader = $sqlcmd.ExecuteReader()
$arry = #()
while ($Reader.Read()) {
$row = #{}
for ($i = 0; $i -lt $reader.FieldCount; $i++)
{
$row[$reader.GetName($i)] = $reader.GetValue($i)
}
#convert hashtable into an array of PSObjects
$arry+= new-object psobject -property $row
}
First, you're executing the query twice. Both ExecuteNonQuery() and ExecuteReader() will execute the query! You do that multiple times in your script.
Second, you can just do this:
$DataTable = New-Object System.Data.DataTable
$DataTable.Load($sqlcmd.ExecuteReader())
Then, if you really don't want to work with a DataTable -- they're more complex than a custom object but really not that bad -- you can do this to convert it to a generic object pretty easily:
$Data = $DataTable | ConvertTo-Csv -NoTypeInformation | ConvertFrom-Csv
This will make everything a string, though, so be sure that's what you want. You might also try this:
$Data = $DataTable | Select-Object -Property <list>
You don't want to use Select-Object * because you'll get extra properties you probably don't want.
This is also executing the query twice:
$sqlcn.Open()
$sqlcmd = $sqlcn.CreateCommand()
$query = "DELETE FROM Employees WHERE EmployeeID = #eid"
$sqlcmd.CommandText = $query
$sqlcmd.Parameters.AddWithValue("#eid", $eid)
$sqlcmd.ExecuteNonQuery()
$adp = New-Object System.Data.SqlClient.SqlDataAdapter $sqlcmd
$data = New-Object System.Data.DataSet
$adp.fill($data) | Out-Null
$sqlcn.Close()
Both $sqlcmd.ExecuteNonQuery() and $adp.fill($data) execute the query! Additionally, ExecuteNonQuery() returns the number of records affected. You could do this:
$sqlcmd.ExecuteNonQuery() | Out-Null
Or this:
[void]$sqlcmd.ExecuteNonQuery()
But what you really should do is verify that the result is what you expect. You shouldn't be getting -1 for INSERT or DELETE statements.
Learn to look up the documentation for the methods you're calling and understand what the possible return values are and why. All the .Net methods are thoroughly documented on MSDN. You can almost always find them by Googling "C# ". You'll find C# examples that can easily be converted to PowerShell, too.

Resources