Archive | Active Directory RSS for this section

Check user credentials and set auto logon

I had several requests from the developers at our company, to enable auto logon for various servers. I knew this was possible through regedit (as explained in this Microsoft support article)

Though, sometimes it needed to be done on (test) servers which reside on a domain and sometimes it needed to be done one (stand-alone) azure servers. Thus I wanted to automate this process.

I need to be able to validate the credentials first. Also I needed to know if a computer is part of a domain. It seems that if the logged on user is a domain account, the environment variable USERDNSDOMAIN is present. In my case this check is enough to determine if a computer is part of a domain or not (as all accounts that are logged on in domain joined computers are domain account, and in cases they are local account, I would want to set the auto logon for that local account).

Thus it is east to check if the computer is part of a domain or not. I just check if $env:USERDNSDOMAIN equals to $null.

Then I’ll do an extra check if $env:COMPUTERNAME equals $env:USERDOMAIN. If so, the computer is definitely not domain joined.

Then I can check the user credentials that were supplied with the Get-Credential command.

Add-Type -AssemblyName System.DirectoryServices.AccountManagement
$Obj = New-Object System.DirectoryServices.AccountManagement.PrincipalContext('machine', $Computer)
If($Obj.ValidateCredentials($Username, $Password) -eq "True")

If the computer is part of a domain, I’ll have the check the credentials with the DC. This can be achieved with this command:

$CurrentDomain = "LDAP://" + ([ADSI]"").distinguishedName
$Domain = New-Object System.DirectoryServices.DirectoryEntry($CurrentDomain,$UserName,$Password)

If the $Domain variable is $null, the supplied credentials don’t work. Otherwise they are correct and the $Domain variable will contain the LDAP path to the domain, the distinguishedName (and other domain related information).

So if the account is verified, in both cases I can set the registry keys

$RegKeyPathWinLogon = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"
Set-ItemProperty -Path $RegKeyPathWinLogon -Name "AutoAdminLogon" -Value "1"
Set-ItemProperty -Path $RegKeyPathWinLogon -Name "DefaultUserName" -Value "$Username"
Set-ItemProperty -Path $RegKeyPathWinLogon -Name "DefaultPassword" -Value "$Password"

You can download the entire script here.

How to get all remote logged on users

I wanted a simple way to get all (remote) logged on (and disconnected) users on all servers in my domain. This way it’s easier for me to see if there are any disconnected sessions still open. In my case I’ve entered them into a SQL database, but for the example on this blog, I will export the list to a CSV file, on a daily basis.

I use the QUERY SESSION command for this purpose (as I found it to be the most quick and reliable one). The command is called as QWINSTA in my code (which is the same as QUERY SESSION) and works from Windows 2012 and up.

In my case I run this directly on my AD controller, so I can query the computers available in the domain. I also added an exclude list (so I won’t query machines that have been shut down, but are still available in the computers list on my domain)

After I get the list of users (with the server name, session name, sessionId and session state), I will filter out empty user names and administrator account(s), because I’m not interested in those sessions.

In the end I will export the list to a CSV file, but this can also easily be exported to a SQL database, web site etc.

Here is the code:

Start-Transcript C:\Logging\GetConnectedUsers.txt

$Servers = (Get-ADComputer -Filter *).Name | Sort-Object
$dt = Get-Date -Format yyyyMMdd
$exportFile = "C:\Logging\$dt ConnectedUsers.csv"
$openSessions = @()

Foreach ($ServerName in $Servers)
{
$ExcludedServers = "EXCLUDESRV01", "EXCLUDESRV02", "EXCLUDESRV03"
If ($ExcludedServers -notcontains $ServerName)
{
Write-Host "Getting session information for $ServerName"
$sessions = qwinsta /server $ServerName| ?{ $_ -notmatch '^ SESSIONNAME' } | %{
$item = "" | Select "ServerName", "SessionName", "Username", "Id", "State"
$item.ServerName = $ServerName
$item.SessionName = $_.Substring(1,18).Trim()
$item.Username = $_.Substring(19,20).Trim()
$item.Id = $_.Substring(39,9).Trim()
$item.State = $_.Substring(48,8).Trim()
$item
}
$openSessions += $sessions | where { ($_.Username -ne "") -and ($_.Username -ne "Administrator") }
}
Else { Write-Host "Skipping named computer $ServerName" }
}

$openSessions | Export-Csv "$exportFile" -NoTypeInformation

Stop-Transcript

And the complete script can be downloaded here.

Writing to a SQL database

Last week I needed to store information in a SQL database, so I can share certain user account information with a select group of users in the helpdesk department, but they shouldn’t have access to the AD itself. So I decided that a SQL database was the way to go and I can create a gridview in Powershell for them, to retrieve this information for now, while our programmers change their helpdesk tool to include this information in there.

I decided that I wanted to update this information every 15 minutes and wanted to clear the database everytime (so I don’t need to be bothered with deleting old users and changing ones that have been changed), before I filled it with my data. I can use Integrated Security, because the AD account that runs the script has the correct access rights to the SQL database and my SQL server is set to Windows Authentication mode.

$conn = New-Object System.Data.SqlClient.SqlConnection
$conn.ConnectionString = "Data Source=SQLSERVER(\INSTANCE);Initial Catalog=DatabaseName;Integrated Security=SSPI;"
$conn.open()
$cmd = New-Object System.Data.SqlClient.SqlCommand
$cmd.connection = $conn
$cmd.commandtext = "TRUNCATE TABLE TableName"
If($cmd.executenonquery() -eq -1) { Write-Host "Succesfully cleared table" }
$conn.close()

If you were to use the above code, change the SQLSERVER text to the name of your SQL server, if needed add the backslash and the instance name and change the text DatabaseName to your database name. Also change the text TableName to your table name.

Once the table is cleared, I get all my desired information from my AD servers. When I get this, I’ll have to put it in the SQL database. Remember that when adding data to a SQL table, you need to specify each column name you’d want to fill with your data (usually all column names of the table, but you might have columns which allow nulls, and if you don’t need to fill those with data, you don’t need to enter those names. In my case all columns need to be filled (as they are all the data I need to share from my AD with our helpdesk people), so I entered all my column names.

$conn = New-Object System.Data.SqlClient.SqlConnection
$conn.ConnectionString = "Data Source=SQLSERVER(\INSTANCE);Initial Catalog=DatabaseName;Integrated Security=SSPI;"
$conn.open()
$cmd = New-Object System.Data.SqlClient.SqlCommand
$cmd.connection = $conn
$cmd.commandtext = "INSERT INTO TableName (OuName,UserName,AccountEnabled,EmailAddress,LastLogonTime,LogonCount) VALUES('{0}','{1}','{2}','{3}','{4}','{5}')" -f $ouName,$userName,$accountEnabled,$emailAddress,$lastLogonDateTime,$logonCount
If($cmd.executenonquery() -eq 1) { Write-Host "Successfully added $ouName, $userName, $accountEnabled, $emailAddress, $lastLogonDateTime, $logonCount to the SQL database" }
$conn.close()

In case you wonder how to get the logonCount and emailAddress from an AD user, those are extra properties which can be requested with the Get-ADUser command in powershell, like this:

Get-ADUser $userName -Properties logonCount,emailAddress

The lastLogon information isn’t found in the Get-ADUser command, but can be found trhough the Get-ADObject command, like this:

Get-ADUser $userName | Get-ADObject -Properties lastLogon

And that’s all there’s to it, to write to SQL and clearing the table.