Tag Archives: Server 2008 R2

Find user session on all active servers

If you want to find a session on all active servers in the domain you can make use of the command quser /server:whatever. This displays the session details from that server. This is handy if some administrator has locked his account because of old sessions on some server. There’re alternative ways to find this. I made a post awhile back. See it here.

IPMO ActiveDirectory

$Username = "SAMACCOUNTNAME"
$List2 = @()

$List = Get-ADComputer -Properties passwordlastset,operatingsystem -Filter * | ?{$_.PasswordLastSet -gt (Get-Date).Adddays(-30) -and $_.OperatingSystem -match "Server"}

Foreach ($item in $list){
	[String]$Server = $item.Name
	$Result =@( (quser /server:$Server) -replace '\s{2,}',','|ConvertFrom-Csv)
		Foreach ($object in $result){
			If ($Object.UserName -Match $Username){$Output = ($Server + ";" + $Object.UserName + ";" + $Object.State)
			$List2 += $Output
		}
	}
} 

Compare AD User Properties

An administrator had to resolve an issue with a user account that could not sync with Share file. He thought that he had tried everything and so wanted to compare an Active Directory user that he copied from the trouble account with the actual user. The copied user did not have any trouble. After comparing he found that the user was member of an Active Directory Protected Group (aka Admincount=1). I made a Powershell script for him so he could compare the two.

Write-Host "Dit script vergelijkt twee Active Directory Users met elkaar."
$Outputdir = ".\exports\"
$Continue = "Y"
While ($Continue -eq "Y"){

While ($User1 -eq $Null){
$User1Value = Read-Host "Geef eerste user op"
$User1 = Get-ADUser $User1Value -Properties *}

While ($User2 -eq $Null){
$User2Value = Read-Host "Geef tweede user op"
$User2 = Get-ADUser $User2Value -Properties *
}

	$Usercomparison = @()
	$Usercomparison2 = @()

	$User1.GetEnumerator() | % {
		If ($User2.($_.Key) -eq $_.Value.value)
		{
			$Comparison = 'Gelijk'
		}
		else
		{
			$Comparison = 'Verschilt'
		}

		$UserObject = New-Object PSObject -Property ([ordered]@{
			Property = $_.Key
			$User1Value = $_.Value
			$User2Value = $User2.($_.Key)
			Overeenkomst = $Comparison
		})
		$UserObject2 = New-Object PSObject -Property ([ordered]@{
			Property = $_.Key
			$User1Value = $_.Value.Value
			$User2Value = $User2.($_.Key)
			Overeenkomst = $Comparison
		})
		$UserComparison += $UserObject
		$UserComparison2 += $UserObject2
	}

$UserComparison


while ($Difference -notmatch "[y|n]"){$Difference = read-host "Wil je filteren op gegevens die verschillen?(Y/N)"}

If ($Difference -eq "Y"){$UserComparison | ? {$_.Overeenkomst -match "Verschilt"} | FT}

while ($Choice -notmatch "[y|n]"){$choice = read-host "Wil je een export maken van de ongefilterde gegevens? (Y/N)"}
If ($Choice -eq "Y"){
Write-Host "Er wordt een export gemaakt van de bovenstaande gegevens op de locatie:"
If ((Test-Path ("$OutputDir"+"UserComparison-"+"$User1Value"+"$User2Value"+".csv")) -eq $True){
$List = GCI "$OutputDir" | ? {$_.FullName -match ("UserComparison-"+"$User1Value"+"$User2Value")}
[int]$Count = $list.count
$Count++}
If ($Count -ne $null){[string]$Count = ("-"+"$Count")}
$UserComparison2 | Export-csv -Path ("$OutputDir"+"UserComparison-"+"$User1Value"+"$User2Value"+"$Count"+".csv") -NoTypeInformation -Delimiter ";"
$Item = GI ("$OutputDir"+"UserComparison-"+"$User1Value"+"$User2Value"+"$Count"+".csv")
$Item.FullName
}
Remove-Variable User1,User1value,User2,User2value,Choice,Continue,Difference
While ($Continue -notmatch "[y|n]"){$Continue = Read-Host "Wil je nog een vergelijking uitvoeren? (Y/N)"}
}

Resolving script for autocomplete

A servicedesk employee came to me with a problem with the autocomplete functionality in Outlook 2010. So I looked around for a bit to find some code. I came across a script that does the job. I added functionality so that it would work for the servicedesk. For the script to work, the user will need to have enough permissions to add permissions to any mailbox. The script is semi-automatic. So user interaction is needed.

Outlook 2003 used to have NK2 files which are corrupt from time to time. Then Microsoft replaced that functionality into a .dat with exact the same issues. Clearly in Outlook 2010 this problem still exists. The key difference is that Outlook 2010 and Outlook 2013 in combination with Exchange 2010 or later has a functionality called suggested contacts. This works similar to the autocomplete function only better. Contacts added to the suggested contacts will be in the autocomplete list. Imagine that by generating a list of all unique recipients in the sent item you’ll have a list that is satisfactory for the user.

Furthermore this functionality works regardless of the user profile. So in our case if a user uses Outlook Web App that user will also have that same functionality. There is a catch. This does not work as intended in combination with Res Workspace Manager and Zero-profiling. To get this right you can use another script.

This script gives write the LegacyDN as output on screen. This is needed for situaties where you have GAL segregation. Then it opens the control panel for e-mail on that server. Of course Outlook needs to be installed on the system. That you need to permit the script to use Outlook. You’ll get the warning as seen below:

Screen Shot 2015-04-16 at 13.55.28

Again if you use RES Workspace Manager with Zero-Profiling use another script.

#Import Modules,Snapins and functions
ipmo ActiveDirectory
Add-PSSnapin Microsoft.Exchange.Management.Powershell.E2010
Function AddTo-SuggestedContactsIfNotAlreadyAdded ($name, $email)
{

    if(($name -eq "") -or ($email -eq "") -or ($name -eq $null) -or ($email -eq $null)){
        return;
    }

    $name = $name.Replace("'", "").Replace("""", "")


    $contactAlreadyAdded = $false

    foreach ($elem in $global:alreadyAddedEmails) {
        if(($elem.ToLower() -match $email.ToLower())){
            $contactAlreadyAdded = $true
            Write-Host  ($global:counter)"/"($totalItems)  "SKIPPED " $name.PadRight(25," ") "-" $email
            return;
        }
    }

    if(!$contactAlreadyAdded )    {
        $newcontact = $contacts.Items.Add()
        $newcontact.FullName = $name
        $newcontact.Email1Address = $email
        $newcontact.Save()
        $global:alreadyAddedEmails += $email
        Write-Host ($global:counter)"/"($totalItems)  "ADDED   " $name.PadRight(25," ") "-" $email
    }
}

#Set Variables
$Admin = [Environment]::UserName
$choice2 = "y"
$Admindomain = $env:userdomain

#Set loop function
while ($choice2 -match  "y"){

#Request Username from interactive User
Write-Host "Voer de gebruikersnaam van de gebruiker in: " -NoNewline -F Cyan 
$Username = read-host 

#Test if user exist
$Testresult = $Null
$Testresult = Get-ADUser $Username -Properties *
While ($Testresult -eq $Null){
write-host "De opgegeven Username bestaat niet of is foutief ingevoerd. Toets de username in in het format zoals bijvoorbeeld OTHSBE02" -b Black -f Red
$Username = Read-Host "Voer de gebruikersnaam van de gebruiker in:" -NoNewline -F Cyan 
$Testresult = Get-ADUser $Username -Properties *}

Get-ADUser -Identity $Testresult.samaccountname -Properties legacyExchangeDN  | Select legacyExchangeDN

#Addmailbox permission to the admin user with retry
Add-MailboxPermission -Identity $Testresult.samaccountname -AccessRights "FullAccess" -User ("$AdminDomain"+"\"+"$Admin")
$Testresult2 = $null
$Testresult2 = Get-MailboxPermission -Identity $Testresult.samaccountname | ? {$_.User -match ("$AdminDomain"+"\\"+"$Admin")}
While ($Testresult2 -eq $Null){
write-host "De rechten op de mailbox zijn niet goed gezet. Het wordt nogmaals geprobeerd" -b Black -f Red
Add-MailboxPermission -Identity $Testresult.samaccountname -AccessRights "FullAccess" -User ("$AdminDomain"+"\"+"$Admin")
$Testresult2 = Get-MailboxPermission -Identity $Testresult.samaccountname | ? {$_.User -match ("$AdminDomain"+"\\"+"$Admin")}}


#Opens Mail control panel
C:\Windows\SysWOW64\control.exe mlcfg32.cpl

#Confirm that the admin have changed to Exchange profile accordingly.
$choice = $Null
 while ($choice -notmatch "[y|n]"){
     $choice = read-host "Heb je het Outlook profiel aangepast? (Y/N)"
      }


$outlook = new-object -com outlook.application
$olFolders = "Microsoft.Office.Interop.Outlook.OlDefaultFolders" -as [type]
$namespace = $outlook.GetNameSpace("MAPI")
$sentItems = $namespace.getDefaultFolder($olFolders::olFolderSentMail)
$alreadyAddedEmails = @() #Empty Array
$counter = 0;
$totalItems = $sentItems.items.count;

Write-Host "Scanning through" $totalItems "emails in SentItems"

$contacts = $outlook.Session.GetDefaultFolder($olFolders::olFolderSuggestedContacts)



# Loop through all emails in SentItems
$sentItems.Items | % {

    #Loop through each recipient
    $_.Recipients | %{
        AddTo-SuggestedContactsIfNotAlreadyAdded $_.Name  $_.Address
    }
    $global:counter = $global:counter + 1
}

$outlook.Quit()

#Addmailbox permission to the admin user with retry
Remove-MailboxPermission -Identity $Testresult.samaccountname -AccessRights "FullAccess" -User ("$AdminDomain"+"\"+"$Admin")
$Testresult2 = Get-MailboxPermission -Identity $Testresult.samaccountname | ? {$_.User -match ("$AdminDomain"+"\\"+"$Admin")}
While ($Testresult2 -ne $Null){
write-host "De rechten op de mailbox zijn niet goed gezet. Het wordt nogmaals geprobeerd" -b Black -f Red
Remove-MailboxPermission -Identity $Testresult.samaccountname -AccessRights "FullAccess" -User ("$AdminDomain"+"\"+"$Admin")
$Testresult2 = Get-MailboxPermission -Identity $Testresult.samaccountname | ? {$_.User -match ("$AdminDomain"+"\\"+"$Admin")}}


#Present user with the choice to do another user.
$choice2 = read-host "Wilt u dit voor nog een user uitvoeren? (Y/N)"
}

 

Export all tasks in the domain

In the spirit of controlling all tasks you can do an export of them all in your domain. To do this you can use to following script. This script uses the schtasks.exe. It exports the scheduled tasks on any server in the domain. The list of those servers is generated based on when the have logged on to the domain. I explained that in this post.

You can adjust this code to your needs.

Ipmo ActiveDirectory
$Date = (Get-Date).AddDays(-15)
$list = Get-ADComputer -Filter 'OperatingSystem -like "Windows Server 20*" -and LastLogonDate -gt $Date' -Properties LastLogonDate,Operatingsystem
Foreach ($Computer in $list){$Export = $Null
$Export = schtasks /query /FO CSV /V /S $Computer.DNSHostName
$Path = ".\"+$Computer.Name+".csv"
If ($Export -ne $Null){Foreach ($Line in $Export){AC -Value $Line -Path $Path}}
If ($Export -eq $Null){AC -Value $Computer.DNSHostName -Path ".\Error.csv"}}
$CSV = gci .\* -Include *.csv | ? {$_.FullName -notmatch "Error.csv"}
$List = @()
$List += Import-CSV $CSV -Delimiter ","
$List | Export-CSV ".\CompleteExport.csv" -Delimiter ";" -Append -NoTypeInformation

 

Administrator account monitor script

The Administrator account is an account which you must monitor in a managed environment. You do not want this account to be exploited. For example I recently encountered an Administrator account which had a mailbox. To make matters worse the account had ActiveSync enabled. So if someone would authenticate with the Administrator account to Activesync it would become visible when you have the password right. A blackhat hacker could do a long term deployment in which he would try to authenticate multiple times per hour. He would be unnoticed in his attempts as long as he keeps the pace low. Imagine what a massive number of passwords you can try in two years time. If you have chosen a bad Administrator password you could end up being compromised. 105120 may seem like a low number but in reality many companies do not change this password frequently and if those companies started out with a weak password, the chance of being compromised is quite large.

What most companies also don’t do is monitor when someone logged on with this account recently.  Or more importantly when someone reset the password. This information is very important to keep an eye on. I made a script which can be planned to report any of those events by mail. You may adjust as you wish.

I highly recommend to sign this script to prevent rogue administrator scenarios. If you want to monitor any powerful group in your domain I have made a script that does that job similar to to one below

<#Administrator Test Script#>
$MailServer = "mailserver"
$Recipients = "some@one.com","any@body.com"
cd "Path-to-Script..."

$Domaincontrollers = [system.directoryservices.activedirectory.domain]::GetCurrentDomain() | ForEach-Object {$_.DomainControllers} | ForEach-Object {$_.Name} 
$Errors =@()
Foreach ($Domaincontroller in $DomainControllers){$Path = ".\"+$Domaincontroller+".csv"
If ((Test-Path $Path) -eq $False){
Get-ADUser Administrator -Properties * -Server $DomainController | Select Enabled,LastBadPasswordAttempt,LastLogon,LastLogonDate,LastLogonTimeStamp,LockedOut,PasswordLastSet | Export-CSV -Delimiter ";" -Path $Path
}}
$CSV = GCI ".\*" -Include *.csv
Foreach ($Item in $CSV){
$StoredValues = Import-CSV $Item.FullName -Delimiter ";"
$DC = $Item.Name.Replace(".csv",'')
Get-ADUser Administrator -Properties * -Server $DC | Select Enabled,LastBadPasswordAttempt,LastLogon,LastLogonDate,LastLogonTimeStamp,LockedOut,PasswordLastSet | Export-CSV -Delimiter ";" -Path ".\Temp.csv"
$ActualValues = Import-CSV ".\Temp.csv" -Delimiter ";"

$CompareResult =@(Compare -ReferenceObject $StoredValues -DifferenceObject $ActualValues -Property Enabled)
If ($CompareResult -ne $Null){$Errors += "Enabled"+";"+"$DC"+";"+$ActualValues.Enabled+";"+$StoredValues.Enabled}
$CompareResult =@(Compare -ReferenceObject $StoredValues -DifferenceObject $ActualValues -Property LastBadPasswordAttempt)
If ($CompareResult -ne $Null){$Errors += "LastBadPasswordAttempt"+";"+"$DC"+";"+$ActualValues.LastBadPasswordAttempt+";"+$StoredValues.LastBadPasswordAttempt}
$CompareResult =@(Compare -ReferenceObject $StoredValues -DifferenceObject $ActualValues -Property LastLogon)
If ($CompareResult -ne $Null){$Errors += "LastLogon"+";"+"$DC"+";"+$ActualValues.LastLogon+";"+$StoredValues.LastLogon}
$CompareResult =@(Compare -ReferenceObject $StoredValues -DifferenceObject $ActualValues -Property LastLogonTimeStamp)
If ($CompareResult -ne $Null){$Errors += "LastLogonTimeStamp"+";"+"$DC"+";"+$ActualValues.LastLogonTimeStamp+";"+$StoredValues.LastLogonTimeStamp}
$CompareResult =@(Compare -ReferenceObject $StoredValues -DifferenceObject $ActualValues -Property LockedOut)
If ($CompareResult -ne $Null){$Errors += "LockedOut"+";"+"$DC"+";"+$ActualValues.LockedOut+";"+$StoredValues.LockedOut}
$CompareResult =@(Compare -ReferenceObject $StoredValues -DifferenceObject $ActualValues -Property PasswordLastSet)
If ($CompareResult -ne $Null){$Errors += "PasswordLastSet"+";"+"$DC"+";"+$ActualValues.PasswordLastSet+";"+$StoredValues.PasswordLastSet}
Remove-Item ".\Temp.csv" -Force
}

If ($Errors.length -ne 0){$Body = "Er heeft een wijziging plaats gevonden op het account Administrator. De Fout/Fouten zijn:"+"`n`n"
Foreach ($item in $Errors){
$Array = $Item.Split(";")
If ($Array[0] -Match "Enabled"){$Body = $Body + "Op de domaincontroller: "+$Array[1]+" is de status van het account gewijzigd van Enabled: "+$Array[3]+" naar Enabled: "+$Array[2]+".`n"}
If ($Array[0] -Match "LastBadPasswordAttempt"){$Body = $Body + "Op de domaincontroller: "+$Array[1]+" is er een BadPasswordAttempt uitgevoerd op "+$Array[2]+". De waarde was hiervoor: "+$Array[3]+".`n"}
If ($Array[0] -Match "LastLogon"){$StoredValue = $Array[3] ; $StoredValue = [datetime]::fromfiletime($StoredValue) ; $ActualValue = $Array[2] ; $ActualValue =  [datetime]::fromfiletime($ActualValue) ; $Body = $Body + "Op de domaincontroller: "+$Array[1]+" is de waarde van LastLogon gewijzigd van "+$StoredValue+" naar de waarde: "+$ActualValue+".`n"}
If ($Array[0] -Match "LastLogonTimeStamp"){$StoredValue = $Array[3] ; $StoredValue = [datetime]::fromfiletime($StoredValue) ; $ActualValue = $Array[2] ; $ActualValue =  [datetime]::fromfiletime($ActualValue) ; $Body = $Body + "Op de domaincontroller: "+$Array[1]+" is de waarde van LastLogonTimeStamp gewijzigd van "+$StoredValue+" naar de waarde: "+$ActualValue+".`n"}
If ($Array[0] -Match "LockedOut"){$Body = $Body + "Op de domaincontroller: "+$Array[1]+" is de status van het account gewijzigd van LockedOut: "+$Array[3]+" naar LockedOut: "+$Array[2]+".`n"}
If ($Array[0] -Match "PasswordLastSet"){$Body = $Body + "Op de domaincontroller: "+$Array[1]+" is er een PasswordLastSet uitgevoerd op "+$Array[2]+". De waarde was hiervoor: "+$Array[3]+".`n"}
}
Send-MailMessage -From "AdministratorRoles@Domain.com" -Subject "Er heeft een wijziging plaatsgevonden op het Administrator account" -To $Recipients  -Body $Body -SmtpServer $MailServer}

Foreach ($item in $CSV){Remove-item $Item -Force}
Foreach ($Domaincontroller in $DomainControllers){$Path = ".\"+$Domaincontroller+".csv"
Get-ADUser Administrator -Properties * -Server $DomainController | Select Enabled,LastBadPasswordAttempt,LastLogon,LastLogonDate,LastLogonTimeStamp,LockedOut,PasswordLastSet | Export-CSV -Delimiter ";" -Path $Path}




EDIT 30 April 2015

Since the number of e-mails sent is very high, I was asked to change the script to show where the error originated from. I found that in the Security event log on the specified domain controller an error is written with that information. The problem is that it will not export very well. If you select the Message string of that Event log item you can not simply select the originating address.

Screen Shot 2015-04-30 at 10.53.50

The EventID number is 4771. You can see the additional information in the image above. So I changed the script so that it will search the event log of the domain controller on which the authentication failure occurred. You’ll have to make sure that the event log is not too big. I have used this script with an event log that was 1 GB of size. I have had serious problems with the script because it cannot index anywhere from 1.8 million records in the memory. You can get by this to select -Newest 200000 in the Get-EventLog cmdlet. However you’ll have less accuracy as the event log item can be outside that limit in some cases. I do not know this for certain but I have a hunch that Get-EventLog will search from bottom to top. We have used the script successfully with an event log size of 128 MB. Even an event log of that size does pose a problem as querying that does take a lot of time. The best thing to do is the run the script like every 2 minutes and specify the first 50K records.

Anyhow I noticed that there is some difference between that event log per domain controllers. This is just like the case further above that when you query for when the last badpassword attempt has occurred there’s a difference per domain controller. So just like that I made sure that the domain controller on which the bad password attempt occurred is being queried. You need to have Powershell version 3 . To check which version of Powershell is installed enter this code.

<#Administrator Test Script#>
$MailServer = "mailserver"
$Recipients = "some@one.com","any@body.com"
cd "Path-to-Script..."

$Domaincontrollers = [system.directoryservices.activedirectory.domain]::GetCurrentDomain() | ForEach-Object {$_.DomainControllers} | ForEach-Object {$_.Name} 
$Errors =@()
Foreach ($Domaincontroller in $DomainControllers){$Path = ".\"+$Domaincontroller+".csv"
If ((Test-Path $Path) -eq $False){
Get-ADUser Administrator -Properties * -Server $DomainController | Select Enabled,LastBadPasswordAttempt,LastLogon,LastLogonDate,LastLogonTimeStamp,LockedOut,PasswordLastSet | Export-CSV -Delimiter ";" -Path $Path
}}
$CSV = GCI ".\*" -Include *.csv
Foreach ($Item in $CSV){
$StoredValues = Import-CSV $Item.FullName -Delimiter ";"
$DC = $Item.Name.Replace(".csv",'')
Get-ADUser Administrator -Properties * -Server $DC | Select Enabled,LastBadPasswordAttempt,LastLogon,LastLogonDate,LastLogonTimeStamp,LockedOut,PasswordLastSet | Export-CSV -Delimiter ";" -Path ".\Temp.csv"
$ActualValues = Import-CSV ".\Temp.csv" -Delimiter ";"

$CompareResult =@(Compare -ReferenceObject $StoredValues -DifferenceObject $ActualValues -Property Enabled)
If ($CompareResult -ne $Null){$Errors += "Enabled"+";"+"$DC"+";"+$ActualValues.Enabled+";"+$StoredValues.Enabled}
$CompareResult =@(Compare -ReferenceObject $StoredValues -DifferenceObject $ActualValues -Property LastBadPasswordAttempt)
If ($CompareResult -ne $Null){$Errors += "LastBadPasswordAttempt"+";"+"$DC"+";"+$ActualValues.LastBadPasswordAttempt+";"+$StoredValues.LastBadPasswordAttempt}
$CompareResult =@(Compare -ReferenceObject $StoredValues -DifferenceObject $ActualValues -Property LastLogon)
If ($CompareResult -ne $Null){$Errors += "LastLogon"+";"+"$DC"+";"+$ActualValues.LastLogon+";"+$StoredValues.LastLogon}
$CompareResult =@(Compare -ReferenceObject $StoredValues -DifferenceObject $ActualValues -Property LastLogonTimeStamp)
If ($CompareResult -ne $Null){$Errors += "LastLogonTimeStamp"+";"+"$DC"+";"+$ActualValues.LastLogonTimeStamp+";"+$StoredValues.LastLogonTimeStamp}
$CompareResult =@(Compare -ReferenceObject $StoredValues -DifferenceObject $ActualValues -Property LockedOut)
If ($CompareResult -ne $Null){$Errors += "LockedOut"+";"+"$DC"+";"+$ActualValues.LockedOut+";"+$StoredValues.LockedOut}
$CompareResult =@(Compare -ReferenceObject $StoredValues -DifferenceObject $ActualValues -Property PasswordLastSet)
If ($CompareResult -ne $Null){$Errors += "PasswordLastSet"+";"+"$DC"+";"+$ActualValues.PasswordLastSet+";"+$StoredValues.PasswordLastSet}
Remove-Item ".\Temp.csv" -Force
}

If ($Errors.length -ne 0){$Body = "Er heeft een wijziging plaats gevonden op het account Administrator. De Fout/Fouten zijn:"+"`n`n"
Foreach ($item in $Errors){
$Array = $Item.Split(";")
If ($Array[0] -Match "Enabled"){$Body = $Body + "Op de domaincontroller: "+$Array[1]+" is de status van het account gewijzigd van Enabled: "+$Array[3]+" naar Enabled: "+$Array[2]+".`n"}
If ($Array[0] -Match "LastBadPasswordAttempt"){
$Resultlist = @()
$Eventlog=@( Get-EventLog -LogName Security -ComputerName $Array[1] | ?{$_.entrytype -eq "FailureAudit" -and $_.message -match "Administrator"})
Foreach ($Event in $Eventlog){$Message = $Event.message.Split("`n")
Foreach ($Line in $Message){
If ($Line -match "Client Address:"){
[String]$Result = $Line.replace("`t",'').Replace("Client Address:",'').Replace(" ",'').Replace("::ffff:",'')}}
$ReverseName = $Null
[string]$ReverseName = [System.Net.Dns]::GetHostbyAddress($Result.Trim()).hostname
If ($ReverseName -eq $Null){$ReverseName = "Naam kon niet opgehaald worden"}
$Value = "- IP-Adres: "+($Result.replace("`n",'')) + " welke resolved naar $ReverseName`n"
$Resultlist += $Value}
$Resultlist = $Resultlist | Sort -Unique
$Body = $Body + "Op de domaincontroller: "+$Array[1]+" is er een BadPasswordAttempt uitgevoerd op "+$Array[2]+". De waarde was hiervoor: "+$Array[3]+". De attempts kwamen vanaf de volgende computer/Computers:`n`r"
$Body = $Body + $Resultlist + "`n`n"}
If ($Array[0] -Match "LastLogon"){$StoredValue = $Array[3] ; $StoredValue = [datetime]::fromfiletime($StoredValue) ; $ActualValue = $Array[2] ; $ActualValue =  [datetime]::fromfiletime($ActualValue) ; $Body = $Body + "Op de domaincontroller: "+$Array[1]+" is de waarde van LastLogon gewijzigd van "+$StoredValue+" naar de waarde: "+$ActualValue+".`n"}
If ($Array[0] -Match "LastLogonTimeStamp"){$StoredValue = $Array[3] ; $StoredValue = [datetime]::fromfiletime($StoredValue) ; $ActualValue = $Array[2] ; $ActualValue =  [datetime]::fromfiletime($ActualValue) ; $Body = $Body + "Op de domaincontroller: "+$Array[1]+" is de waarde van LastLogonTimeStamp gewijzigd van "+$StoredValue+" naar de waarde: "+$ActualValue+".`n"}
If ($Array[0] -Match "LockedOut"){$Body = $Body + "Op de domaincontroller: "+$Array[1]+" is de status van het account gewijzigd van LockedOut: "+$Array[3]+" naar LockedOut: "+$Array[2]+".`n"}
If ($Array[0] -Match "PasswordLastSet"){$Body = $Body + "Op de domaincontroller: "+$Array[1]+" is er een PasswordLastSet uitgevoerd op "+$Array[2]+". De waarde was hiervoor: "+$Array[3]+".`n"}
}
Send-MailMessage -From "AdministratorRoles@Domain.com" -Subject "Er heeft een wijziging plaatsgevonden op het Administrator account" -To $Recipients  -Body $Body -SmtpServer $MailServer}

Foreach ($item in $CSV){Remove-item $Item -Force}
Foreach ($Domaincontroller in $DomainControllers){$Path = ".\"+$Domaincontroller+".csv"
Get-ADUser Administrator -Properties * -Server $DomainController | Select Enabled,LastBadPasswordAttempt,LastLogon,LastLogonDate,LastLogonTimeStamp,LockedOut,PasswordLastSet | Export-CSV -Delimiter ";" -Path $Path}


Export PasswordNeverExpires for all accounts in the domain

A simple script to export all accounts in the domain that have the PasswordNeverExpires property set on True

ipmo ActiveDirectory 
$list = Get-ADUser -Filter 'PasswordNeverExpires -eq $True' -Properties PasswordNeverExpires | Select Name,SamAccountName,PasswordNeverExpires
$List | Export-CSV ".\PasswordNeverExpires.csv" -Delimiter ";" -Append -NoTypeInformation

You can make it more complex by adding filters based on the OU.If Users that you want to monitor are in the OU’s Accounts,Employees and Admins you can enter the code below. Adjust it to your needs.

ipmo ActiveDirectory 
$list = Get-ADUser -Filter 'PasswordNeverExpires -eq $True' -Properties PasswordNeverExpires | ? {$_.DistinguishedName -match ",OU=Accounts|,OU=Employees|,OU=Admins" | Select Name,SamAccountName,PasswordNeverExpires
$List | Export-CSV ".\PasswordNeverExpires.csv" -Delimiter ";" -Append -NoTypeInformation

Export services

Recently I needed to have an export of all services. To be more specific I needed to have an export of Log On As field of all services. I wanted to have this export for alle computers in de domain. I wanted only servers but you can modify the script as you wish. For this to work you’ll need to have PSRemoting enabled on all servers. I wrote a script that does that job.

I added a LastLogonDate as that can confirm that the specified server is logged on lately. I used 15 days because the LastLogonTimeStamp isn’t a hard value. The Timestamp can fall behind anywhere from 14 to 8 days. So by specifying 15 days you are safe.

Ipmo ActiveDirectory
$Date = (Get-Date).AddDays(-15)
$list = Get-ADComputer -Filter 'OperatingSystem -like "Windows Server 20*" -and LastLogonDate -gt $Date' -Properties LastLogonDate,Operatingsystem

Foreach ($Computer in $List){$Export = $Null
$Export = Get-WmiObject win32_service -ComputerName $Computer.DnsHostName | select Name,Startname,Systemname
If ($Export -ne $Null){$Export | Export-CSV ".\Services.csv" -Delimiter ";" -Append -NoTypeInformation}
If ($Export -eq $Null){AC -Value $Computer.DNSHostName -Path ".\Error.csv"}}

Test whether PS Remoting is enabled

PSRemoting is a powerful way to manage remote computers. I often use this. However on some server PSRemoting is not enabled. A custom function can help you. When you import this in a Powershell session you can manage the server that don’t have PSRemoting enabled. I assume that you only have Windows Server 2008 and above servers in you domain.

First import the module or copy-paste it in you shell.

function Test-PSRemoting 
{ 
    param( 
        [Parameter(Mandatory = $true)] 
        $RemoteComputer 
    ) 
    
    try 
    {
		#Returns value 1 in a scriptblock on the remote computer
        $errorActionPreference = "Stop" 
        $result = Invoke-Command -ComputerName $RemoteComputer {1} 
    } 
    catch 
    { 
        Write-Verbose $_ 
        return $false 
    } 
     
    if($result -ne "1") 
    { 
        Write-Verbose "The remote computer: $RemoteComputer did not return the expected result. Please make sure you have enough privileges to connect to the computer." -f Red
        return $false 
    } 
    
    $true    
}

TestPSRemoting

Then to use it you can use the following code:

Import-Module ActiveDirectory
Import-Module ".\TestPSRemoting.psm1" -Force
$Servers = Get-ADComputer -Filter * -Properties OperatingSystem | Where {$_.OperatingSystem -match "Server"}
Write-host "There are " $Servers.count " servers found"
$DomainUser = ($env:userdomain+"\"+$env:username)
$secureString = Read-Host -AsSecureString "Enter your password"


Foreach ($Server in $Servers){
$Result = $null
$ServerName = $Server.Name
$Result = Test-PSRemoting $Server
If ($Result -eq $False){
Write-Host "PSRemoting will be enabled on server: $Server"
$Date = (Get-Date).AddMinutes(30)
schtasks /create /S $ServerName /RU $DomainUser /RP $secureString /SC Once /SD $date.ToString('dd"/"MM"/"yyy') /ST $date.ToString('HH":"mm') /TN "Enable PSRemoting" /TR "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe Enable-PSRemoting -Force" /F
}}

You need to be an Administrator of course or at least have enough permission to enable psremoting. If PSremoting is disabled an task will be scheduled to enable it within 30 minutes. You can enable PSRemoting remotely using the script. If you want to enable PSremoting on all workstations you’ll need to change the definition to something else. For example use a part of the DestinguishedName if an OU is called Workstations for example.

Good luck!

Get all installed hot fixes and software

An adminstrator wanted to know if a certain KB from Microsoft had been installed. He wanted to know a command that does the job. In a world with DOS you can simply run:

systeminfo

However he also wanted to see if a Xenapp Hotfix had been installed. This of course can’t be done with the command above. So I wrote some code that exports the uninstall information from the local server. The code export x64 and x86 software. If the server is not 64 bit you’ll get errors. That said you’ll probably don’t have Powershell v3 installed on such server.

$List1 = Get-ItemProperty HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*
$List1 += Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*
$List1 = $List1 | Select DisplayName, DisplayVersion, Publisher, InstallDate

Get uptime server

To get the uptime of a server in powershell run the following command:

Get-CimInstance -ClassName win32_operatingsystem | select csname, lastbootuptime

This works for Powershell v3 and v4. To run it against a remote server run the following code:

Get-CimInstance -ClassName win32_operatingsystem -ComputerName SERVERNAME| select csname, lastbootuptime

Please note that this only works if PS Remoting is enabled. To enable psremoting enter the command:

Enable-PSRemoting -Force

You can also type the command:

net statistics Workstation