Category Archives: Storingen

Filtering and Arraying 101 Powershell

So I try to teach my colleagues a bit more about Powershell from time to time. I have advised them a bit about how they can filter values to meet their requirements. Our architect wanted an export of all user accounts. When I simply exported all AD users in the domain he said that the amount of users is way too large. He asked me how this happend and where all those users are coming from? I wanted to give him an export of all enabled users in ActiveDirectory per OU. Actually I just wanted to show him the amount of users per OU. So I made a little script that does that job. However this is an excellent opportunity for someone to start working with Powershell. So I’ve written a little 101 about how they should start.

Step 1

Import the module ActiveDirectory.

IPMO ActiveDirectory

Step 2

You want to know in what OU a user is situated. Maybe the CMDlet Get-ADUser returns this value. By running the command Get-ADUser SAMACCOUNTNAME -Properties * you’ll get all the available properties. To display the methods and functions you can also use this command, in correspondence with the CMDlet Get-Member (Alias GM). You’ll have to run:

Get-ADUser SAMACCOUNTNAME -Properties * | GM

This prints all available methods,functions,properties and so forth. If you see a property, that is name something like, parent or OU you’ll be lucky. Unfortunately in Powershell 1,2 and 3 there isn’t such a property. So you have to find other ways to get the parent OU.

Step 3

Many CMDlets can be used against the registry, file systems, certificate stores or the Active Directory. In this case you’ll still have to know the containing OU. So probably the best way is to use the CanonicalName which contains this information. However that would kill all the fun. In this example DistinguishedName will have to do it. This obviously displays the containing OU. To save the DistinguishedName in a string run:

$Result = (Get-ADUser SAMACCOUNTNAME).DistinguishedName

Step 4

The DistinguishedName contains the CN of a user. You want to filter that out of the string. But how do you do that? Again use GM to see if you can convert the value to it’s parent or the get what is possible with the value.  To do so run the command:

$Result | GM

You can replace the CN as text. This can be really really tricky. To do so run the command in step 3 without the .DistinguishedName property behind it. Next run the command:

$Result.DistinguishedName.Replace(("CN="+$Result.Name+","),'')

There is no right or wrong if the result is the same and it meets your goals. That is an important lesson to learn.

Step 5

To select an item use the CMDlet Get-Item (Alias GI). This CMDlet will let you select any file, folder or object in the registry,file system, certificate store or AD. Actually you can use it against any mounted PSdrive. We still want to know the containing OU. So in this case we will look up the DistinguishedName in the AD. Again use GM to find out what is possible with the output. To do so run:

GI "AD:\$Result" | GM

Step 6

As you can see the property PSParentPath exists. Maybe this displays the required output? Run the command:

(GI AD:\$Result).PSParentPath

Step 7

That output is not how you want it to be. The type is displayed. To be as clean as possible you will have to replace that part of the line. You can use the Replace method like this:

(GI AD:\$Result).PSParentPath.Replace("Microsoft.ActiveDirectory.Management\ActiveDirectory:://RootDSE/",'')

Step 8

To come back to the objective, we’ll have to export all enabled users. There are two ways to accomplish this.

$List = Get-ADUser -Filter * | ? {$_.Enabled -eq $True}

Or

$List = Get-ADUser -Filter {Enabled -eq $True}

Which one is better? The correct answer would be none. The result is what counts. The best thing is running the command that you can remember or that works best for you. In terms of performance the second command would be better. Try this:

(Measure-Command -Expression {$list = Get-ADUser -Filter * | ?{$_.Enabled -eq $True}}).TotalSeconds

(Measure-Command -Expression {$list = Get-ADUser -Filter {Enabled -eq $true}}).TotalSeconds

Step 9

Next you can form a “script” to collect all DistinguishedNames and replace the unwanted text. To do so run:

Foreach ($item in $List){$Result = (GI AD:\$item).PSParentPath.Replace("Microsoft.ActiveDirectory.Management\ActiveDirectory:://RootDSE/",'')
AC -Value $Result -Path ".\Output.csv"}

Step 10

We have a list called Output.csv. The only thing we have to do now is to count the number of occurrences. This is not necessary the best way to do it. You can save this output in an array if you like.  Below there are examples of many different ways to solve this problem. Below is an example of the last bit of a script.

$List2 = GC ".\Output.csv"
$Uniquelist = $list2 | Sort -Unique
Foreach ($UniqueItem in $Uniquelist){
[int]$Count = 0
Foreach ($Item in $list2){If ($Item -eq $UniqueItem){$Count++}}
$Outinfo = "$UniqueItem" + ";" + "$Count"
AC -Value $outinfo -Path ".\Counted OUs.csv"
}

There are lots of ways to get to your destination. I have generated a number of scripts to do this task.

Script 1

IPMO ActiveDirectory
$list = Get-ADUser -Filter * | ? {$_.Enabled -eq $True}
Foreach ($item in $List){$Result = (GI AD:\$item).psparentPath.Replace("Microsoft.ActiveDirectory.Management\ActiveDirectory:://RootDSE/",'')
AC -Value $Result -Path ".\Output.csv"}
$List2 = GC ".\Output.csv"
$Uniquelist = $list2 | Sort -Unique
Foreach ($UniqueItem in $Uniquelist){
[int]$Count = 0
Foreach ($Item in $list2){If ($Item -eq $UniqueItem){$Count++}}
$Outinfo = "$UniqueItem" + ";" + "$Count"
AC -Value $outinfo -Path ".\Counted OUs.csv"
}

This script generated two output files. A part of statics values is replaced.

Script 2

IPMO ActiveDirectory
$list = Get-ADUser -Filter * | ? {$_.Enabled -eq $True}
Foreach ($item in $list){$Result = $item.distinguishedName.Replace(("CN="+$item.Name+","),'')
AC -Value $Result -Path ".\Output.csv"}
$List2 = GC ".\Output.csv"
$Uniquelist = $list2 | Sort -Unique
Foreach ($UniqueItem in $Uniquelist){
[int]$Count = 0
Foreach ($Item in $list2){If ($Item -eq $UniqueItem){$Count++}}
$Outinfo = "$UniqueItem" + ";" + "$Count"
AC -Value $outinfo -Path ".\Counted OUs.csv"
}

This script contains a replace of dynamic content. You have to be very careful using such a replace. We have CN’s with a value like Berg, Steven van den. That comma is precisely the problem. To display a comma in the DistinguishedName a break is inserted. So the actual value is CN=Berg\, Steven van den,. Based on the specified condition this does not apply. Meaning that you will have inconsistent results. In script 3 this is corrected.

Script 3

IPMO ActiveDirectory
$list = Get-ADUser -Filter {Enabled -eq $True}
$List2 = @()
Foreach ($item in $list){$Result = $item.distinguishedName.Replace('\','').Replace(("CN="+$item.Name+","),'')
$List2 += $Result}
$Uniquelist = $list2 | Sort -Unique
Foreach ($UniqueItem in $Uniquelist){
[int]$Count = 0
Foreach ($Item in $list2){If ($Item -eq $UniqueItem){$Count++}}
$Outinfo = "$UniqueItem" + ";" + "$Count"
AC -Value $outinfo -Path ".\Counted OUs.csv"
}

Here the conditions are met. I have also included an empty array at the line 3. Next all values are added to that array. So only the correct file is exported. Be aware that is you do a double ” after the replace of the special character you’ll need a double backslash. In this case something like .Replace(“\\”,””). The backslash means a break stating a special character.

Even though this script delivers the correct output, I would still advise you to refrain from replacing dynamic content unless you are really sure that every condition is met and that you not filter out too much.

Script 4

IPMO ActiveDirectory
$list = (Get-ADUser -Filter {Enabled -eq $True}).DistinguishedName | %{(GI "AD:\$_").PSParentPath.Replace("Microsoft.ActiveDirectory.Management\ActiveDirectory:://RootDSE/",'')}
$Uniquelist = $list | Sort -Unique
Foreach ($UniqueItem in $Uniquelist){
[int]$Count = 0
Foreach ($Item in $list2){If ($Item -eq $UniqueItem){$Count++}}
$Outinfo = "$UniqueItem" + ";" + "$Count"
AC -Value $outinfo -Path ".\Counted OUs.csv"
}

Script 1 uses the CMDlet Get-Item (Alias GI) to collect the parent OU. In script 1 this is done in separate commands. You can combine this in one line let the example above. I have done this by using the CMDlet Foreach-Object (Alias %).

Script 5

IPMO ActiveDirectory
$list = (Get-ADUser -Filter {Enabled -eq $True}).DistinguishedName | %{(GI "AD:\$_").PSParentPath.Replace("Microsoft.ActiveDirectory.Management\ActiveDirectory:://RootDSE/",'')}
Foreach ($UniqueItem in ($List | Sort -Unique)){
[int]$Count = 0
Foreach ($Item in $list){If ($Item -eq $UniqueItem){$Count++}}
$Outinfo = "$UniqueItem" + ";" + "$Count"
AC -Value $outinfo -Path ".\Counted OUs.csv"
}

Unlike script 4 in shorted the script even further by not generation a separate unique item list.

Script 6

IPMO ActiveDirectory
$list = (Get-ADUser -Filter {Enabled -eq $True}).DistinguishedName | %{(GI "AD:\$_").PSParentPath.Replace("Microsoft.ActiveDirectory.Management\ActiveDirectory:://RootDSE/",'')}
Foreach ($UniqueItem in ($List | Sort -Unique)){
[int]$Count = 0
Foreach ($Item in $list){If ($Item -eq $UniqueItem){$Count++}}
AC -Value ("$UniqueItem" + ";" + "$Count") -Path ".\Counted OUs.csv"
}

The output string is combined in one line.

Script 7

IPMO ActiveDirectory
Get-ADUser -Filter {Enabled -eq $True} | % {($_).distinguishedName.Replace('\','').Replace(("CN="+$_.Name+","),'')} | Group | Select Name,Count | epcsv ".\Counted OUs.csv" -Delimiter ";" -NoTypeInformation

This example is even prettier. Nearly a oneliner. To count we use the Group-Object (Alias Group) te count the number of occurrences. Next we Select the fields we want. Followed by exporting the data by Export-CSV (Alias epcsv). This can be done with that other command aswel.

Script 8

IPMO ActiveDirectory
(Get-ADUser -Filter {Enabled -eq $True}).DistinguishedName | %{(GI "AD:\$_").PSParentPath.Replace("Microsoft.ActiveDirectory.Management\ActiveDirectory:://RootDSE/",'')} | Group | Select Name,Count | epcsv ".\Counted OUs.csv" -Delimiter ";" -NoTypeInformation

In this case you have few ways to select the data. Like these:

IPMO ActiveDirectory
Get-ADUser -Filter {Enabled -eq $True} | %{(GI AD:\$_).PSParentPath.Replace("Microsoft.ActiveDirectory.Management\ActiveDirectory:://RootDSE/",'')} | Group | Select Name,Count | epcsv ".\Counted OUs.csv" -Delimiter ";" -NoTypeInformation

Or

IPMO ActiveDirectory
Get-ADUser -Filter {Enabled -eq $True} | %{(GI AD:\$_).PSParentPath.Replace("Microsoft.ActiveDirectory.Management\ActiveDirectory:://RootDSE/",'')} | Group |  % { AC -Value ("$($_.Name)" + ";" + "$($_.Count)") -Path ".\Counted OUs.csv"}

Or

IPMO ActiveDirectory
Get-ADUser -Filter {Enabled -eq $True} | %{(GI AD:\$_).PSParentPath.Replace("Microsoft.ActiveDirectory.Management\ActiveDirectory:://RootDSE/",'')} | Group  | % { AC -Value ($($_.Name) + ";" +  ($($_).Group.Count)) -Path ".\Counted OUs.csv"}

Except one, these scripts all generate the same output. Know that there are many more ways to get the same output. Again the best method is the one you can remember and you feel comfortable using. If you like oneliners you’ll prefer script 8. If you like to have an overview of what is happening you’ll prefer script 1. Based on your skills in scripting/Powershell you’ll choose anything in between.

You could also have selected CanonicalName. This would have been a lot easier as it doesn’t contain any breaking (\). Another advantage would be that you can sort alphabetically. Since DistinguishedName is formed from the deepest to the highest level is it only possible to sort based on the last OU. Normally you would want to have this export correspond with the hierarchy of Active Directory. This is only possible with CanonicalName. CanonicalName is built up from highest in the hierarchy to the lowest container.

Reference table

Alias CMDlet
IPMO  Import-Module
 GM  Get-Member
 GI  Get-Item
 ?  Where-Object
AC  Add-Content
 GC  Get-Content
 Sort Sort-Object
 % Foreach-Object
 Group  Group-Object
 Select  Select-Object
epcsv  Export-CSV

I hope you’ve had fun!

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)"
}

 

Search for specific text in files on a location

Sometimes a administrator is forced to do some cleaning up that should have been done a million years ago. When previous administrators have left you a nice bucket of history in your environment. A customer of mine used to have a few fileservers around that aren’t there anymore. In DNS those names are resolving to a new CIFS server. However that location is going to change again and now we are forced to clean up that old mess. Of course I’m not going to do the job of opening a few hundred thousand .ini, .config, .cfg and so forth myself. I created a script that does the job and can be delegated to someone else who has more time than me.

Clear
$Outputdir = \\Outputdirpath


$Location = Read-Host "Voer het volledige DFS pad in, in UNC Format bijvoorbeeld:\\DFSFolder"
$TestResult = Test-Path $Location
While ($Testresult -ne $true){
write-host "De opgegeven locatie bestaat niet. Voor opnieuw in" -b Black -f Red
$Location = Read-Host "Voer de DFS Locatie in in UNC Format bijvoorbeeld:\\DFSFolder"
$TestResult = Test-Path $Location}

$Extensions =@(Read-Host 'Voer een extensie in.De Extensie moet een . bevatten')
While ($Extensions -notmatch "."){$Extensions =@(Read-Host 'Voer een extensie in.De Extensie moet een . bevatten')}
$Choice = $null
While ($Choice -notmatch "[y|n]"){$Choice = read-host "Wilt u nog een Extensie toevoegen (Y/N)"
If ($Choice -match "Y"){While ($Extension -notmatch "."){$Extension =(Read-Host 'Voer een extensie in')}
$Extension += $Extension
$Choice = $Null}}

Remove-Variable Choice
$MatchList =@(Read-Host 'Voer een waarde in waaraan voldaan moet worden. Voor een drive mapping is een dubbele backslash vereist')
$Choice = $null
While ($Choice -notmatch "[y|n]"){$Choice = read-host "Wilt u waarde opgeven (Y/N)"
If ($Choice -match "Y"){$MatchItem =(Read-Host 'Voer een waarde in waaraan voldaan moet worden. Voor een drive mapping is een dubbele backslash vereist')
$MatchList += $MatchItem
$Choice = $null}}

$PathName = GI $Location
If ($PathName.Parent.Name -eq $Null){$ExportFile = "$OutputDir\ContentExport-"+ $PathName.Name + ".csv"}
If ($PathName.Parent.Name -ne $Null){$ExportFile = "$OutputDir\ContentExport-"+ $PathName.Parent.Name +"-"+ $PathName.Name +".csv"}

$List = gci $Location -Recurse | Where {$Extensions -match $_.Extension}
Foreach ($Item in $List){$Content = Get-Content $Item.fullname
$Pad = $item.fullname
Foreach ($Extension in $Extensions){If ($Content -match $Extension){Add-Content -Value  "Er is een verwijzing naar $Extension gevonden in: $Pad" -Path $Exportfile}}}

For those who do not speak Dutch very well. If you are searching for a drive mapping you should enter a double slash. The rest you’ll have to find out yourself 😉

Office 2010,2013 activation problem

For a customer I prepared an image. This image contained  a 32 bit version of Office 2010. I needed to enter a key for the installation. At that point I did not had access to their  volume licensing. I wanted to complete the image as soon as possible. That is why I entered a MSDN key. I was unaware at that point that this would cause many problems. First of all the deployed image at that point didn’t accept MAK keys. When I entered the MAK key I received an error that the key was incorrect. After an hour of troubleshooting I downloaded the VL version of Office 2010. I clicked on install. After install Office 2010 accepted my key. However it didn’t activate. I had to call Microsoft Activiation Center in order to activate the key. I asked them why automatic activation did not work. They said that I had to contact MSDN Activation Support. I put the two together and knew that this was caused by the MSDN key that somewhere was activated. I looked for an activation key in the registry somewhere. After deleting some keys I could activate. The keys are:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office\14.0\Registration

And if you installed the 32 bit Office 2010:

HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Office\14.0\Registration

Do this before you install Office 2010 VL.

Find old share names in files on Apps share

A client was talking about how he couldn’t quit sharing a certain share in his Citrix environment. There were to many connections still to that share. So I suggested that I could help him by making a script that looks for certain values in .ini, .log, .cmd, .bat, .cfg, .config etc files. You can adjust this to your own preference.

I did a check of the drive mapping G:\. Please note that a lot of log files contain Logging: which matches G:. That is why I did a match on G:\. In Powershell the ‘\’ character  is an expression followed by a special character which in this case will be \. That is why you need a double slash.

$Locatie = "\\ShareName" 
$Exportfile = ".\Exportfile.csv"
$List = gci $Locatie -Recurse | Where {$_.Extension -match ".ini" -or $_.Extension -match ".cfg" -or $_.Extension -match ".config" -or $_.Extension -match ".inc" -or $_.Extension -match ".bat" -or $_.Extension -match ".cmd" -or $_.Extension -match ".log" -or $_.Extension -match ".txt"}
Foreach ($item in $list){$Content = Get-Content $item.fullname
$Pad = $item.fullname
If ($Content -match "G:\\"){Add-Content -Value  "Er is een verwijzing naar G:\ gevonden in: $Pad" -Path $Exportfile}
If ($Content -match "G:/"){Add-Content -Value  "Er is een verwijzing naar G:/ gevonden in: $Pad" -Path $Exportfile}
If ($Content -match "ServerName1"){Add-Content -Value  "Er is een verwijzing naar ServerName1 gevonden in: $Pad" -Path $Exportfile}
If ($Content -match "ServerName2"){Add-Content -Value  "Er is een verwijzing naar ServerName2 gevonden in: $Pad" -Path $Exportfile}
If ($Content -match "ServerName3"){Add-Content -Value  "Er is een verwijzing naar ServerName3 gevonden in: $Pad" -Path $Exportfile}
If ($Content -match "ServerName4"){Add-Content -Value  "Er is een verwijzing naar ServerName4 gevonden in: $Pad" -Path $Exportfile}
If ($Content -match "ServerName5"){Add-Content -Value  "Er is een verwijzing naar ServerName5 gevonden in: $Pad" -Path $Exportfile}
If ($Content -match "ServerName6"){Add-Content -Value  "Er is een verwijzing naar ServerName6 gevonden in: $Pad" -Path $Exportfile}}

Replace text in local TNSNames.ora with Powershell

An administrator came to me today. Recently they had migrated from Oracle 10 to Oracle 11 and from HP UX to ODA (Oracle Database Appliance). Due to the fact that in this organization there are over 2000 databases, some applications wheren’t tested. In some cases application server look at the wrong TNSNames.ora. The request was to replace the text after host for the approriate host. I had the ‘luck’ that this request only included servers.

I created a that resolve his problem. The script contacts the ActiveDirectory for the list of “servers”. Then the script test if that server has an Oracle directory under C:\. Change this if you have Oracle installed under a different folder. Next there’s a check under the Oracle Directory for any folder that match Ora. These folders are included in an array. The next thing that will be checked is, if the TNSNames.ora exists in the usual folder. If that is the case the script continues. If it doesn’t exist the script will continue with the next folder or server.

Then the content of the TNSNames.ora will be loaded into the memory. The script renames the original TNSNames.ora to TNSNames.old. Or if that file is already in use in some other extension. Then all lines in the TNSNames will be check on the existance of few specific words and those words will be replaced with something else. At last the array NewTNSNames will be written to the location.

 

Import-Module ActiveDirectory
$ADComputers = Get-ADComputer -Filter * -Properties * | Where {$_.OperatingSystem -match "Server"}
$Computers =@()
ForEach ($ADComputer in $ADComputers){$ADComputer = $ADComputer.DNSHostName
If((Test-Path "\\$ADComputer\C$\Oracle") -eq $True){$Computers += $ADComputer}}
ForEach ($Computer in $Computers){$Folders =@( gci "\\$Computer\c$\Oracle\Ora*")
ForEach ($Folder in $Folders){If ((Test-Path "$Folder\Network\Admin\TNSNames.ora") -eq $True){$TNSNames = get-content "$Folder\Network\Admin\TNSNames.ora"
If ((Test-Path "$Folder\Network\Admin\TNSNames.old") -eq $False){Rename-item -Path "$Folder\Network\Admin\TNSNames.ora" -NewName "$Folder\Network\Admin\TNSNames.old"}
If ((Test-Path "$Folder\Network\Admin\TNSNames.NaChangeAugustus2014") -eq $False){Rename-item -Path "$Folder\Network\Admin\TNSNames.ora" -NewName "$Folder\Network\Admin\TNSNames.NaChangeAugustus2014"}
$NewTNSNames =@()
ForEach ($Item in $TNSNames){
If ($Item -match "odatst101a"){$NewTNSNames += $Item.replace("odatst101a","RobIsDeBarbecueKoning.local")}
If ($Item -match "odatst101b"){$NewTNSNames += $Item.replace("odatst101b","RobIsDeBarbecueKeizer.local")}
If ($Item -notmatch "odatst101a" -and $Item -notmatch "odatst101b"){$NewTNSNames +=  $Item}}
ForEach ($Item in $NewTNSNames){Add-Content -Value $Item -Path "$Folder\Network\Admin\TNSNames.ora" }}}}

Registry permissions of users by mistake overwritten

An administrator applied an GPO which overwrites the permissions on the registry of users by mistake.  This results in users who cannot login after they lost permissions on their ntuser.dat registry hive.

We had an immediate problem with the users so decided to restore the entire terminal profiles share. However you can choose to solve the problem. I’ve written a script really quite to at least know how big the problem really is.

We have PSremoting enabled on all terminal servers. Which gave us the posibility to run the script on the entire environment.

Import-Module ActiveDirectory
$Computers = Get-ADComputer -Filter * | Where {$_.Name -match "TERMINALSERVERPREFIX"}

Foreach ($Computer in $Computers){

Enter-Pssession $Computer
 
New-PSDrive -PSProvider Registry -Name HKU -Root HKEY_USERS
 Set-Location HKU:
 $list = GCI
 $Hostname = Hostname
 $Export = "C:\Permissions"+"_"+"$Hostname"+".csv"
 Foreach ($item in $list){$ACLs = Get-ACL $Item.PSChildName | Foreach-Object { $_.Access }
 Foreach ($ACL in $ACLs){
 If ($ACL.IdentityReference -match "ADGROUP Or User"){$Value = $Item.Name + ";" + $ACL.IdentityReference + ";" + $ACL.AccessControlType + ";" + $ACL.IsInherited + ";" + $ACL.InheritanceFlags + ";" + $ACL.PropagationFlags + ";" + $ACL.FileSystemRights
 Add-Content -Value $Value -Path $Export}}}
 Set-Location C:
 Copy-Item -Path $Export -Destination "\\Shared Location"

}

This writes back the SID on which an Active Directory Group had permissions on. You can of course use the information given but you have the compare the SID’s with your Active Directory SID.

Exporting the SID’s of your AD can be done by running the following code:

Import-Module ActiveDirectory
$Export = "C:\SIDS.csv"
$List = Get-ADUser -filter * | Select Name,SID
$Header = "Name;SID"
Add-Content -Value $Header -Path $Export
Foreach ($Item in $list){
$Outinfo = $item.Name + ";" + $Item.SID
Add-Content -Value $Outinfo -Path $Export}

Or

Import-Module ActiveDirectory
$Export = "C:\SIDS.csv"
$List = Get-ADUser -filter * | Select Name,SID
$list | Export-CSV -Path $Export -Delimiter ";" -NoTypeInformation 

Users being disconnected randomly

Some users reported that their sessions where being disconnected. The warning that was stated was that the peer disconnected. On the The XenApp servers reported error 4105. The Terminal Server License server was member of the group Terminal Server License Server. There’s a KB article for this problem.

If you want to export the users that could expierence this problem you can run the script below. In order to export the right group you need to have the default SID for this built in group. This SID is: “S-1-5-32-561″. The right value that the user must have is:”5805bc62-bdc9-4428-a5e2-856a0f4c185e”.

 

Import-module ActiveDirectory
$List2 =@()
$File = ".\Export.csv"
$Header = "DN;ActiveDirectoryRights;InheritanceType;ObjectType;InheritedObjectType;ObjectFlags;AccessControlType;IdentityReference;IsInherited;InheritanceFlags;PropagationFlags"
Add-Content -Value $Header -path $file
$list = get-aduser -Filter * | select DistinguishedName
foreach ($item in $list){$list2 +=$item.DistinguishedName}
foreach ($item in $list2){
$ACLs =@((get-Acl "AD:\$item")| ForEach-Object {$_.Access} | Where {$_.Identityreference -eq "S-1-5-32-561"})
IF ($ACLs.count -gt "0"){
[int]$Count = 0
Foreach ($ACL in $ACLS){IF ($ACL.Objecttype -match "5805bc62-bdc9-4428-a5e2-856a0f4c185e"){$count++}}
IF ($Count -eq "0"){
Foreach ($ACL in $ACLs){
$OutInfo = $item  + ";" + $ACL.ActiveDirectoryRights  + ";" + $ACL.InheritanceType + ";" + $ACL.ObjectType + ";" + $ACL.InheritedObjectType + ";" + $ACL.ObjectFlags + ";" + $ACL.AccessControlType  + ";" + $ACL.IdentityReference   + ";" + $ACL.IsInherited   + ";" + $ACL.InheritanceFlags   + ";" + $ACL.PropagationFlags
Add-content -Value $outinfo -path $File}}}}

Account locked Out Randomly

A user was reporting that at random his AD Account was locked. He ensured me that he did not type his password wrong. After the first six reset I did not believe him. The user was an Administrator so I asked him if by any chance he installed any service under his name. He did change his password some time ago. We decided to revert to his old password. However the problem still occurred. I looked further into the problem. First searching why his account was locked out. On the first domain controller I ran a command:

Get-EventLog -LogName Security | ?{$_.entrytype -eq "FailureAudit" -and $_.message -match "SAMACCOUNTNAMEOFUSER"}

Change the SAMACCOUNTNAMEOFUSER to the sAMAccountname of the user. Add | FL for a larger output of the error.

This retured a set of four result. Three where at the exact same time one was some time after. The message was Kerberos Authentication Failed. The last time was the time that the user came to me saying his account was locked out again.

At the client address was an IP-address specified. It seemed that the Admin had an inactive session on that server or an scheduled task of some sort. Maybe even a service under his account.

I could not enter a pssession to that specific server. So I used a good old DOS Command:

query user /server:IPOFTHATSERVER

It retured the following output:

OutputQueryUser

This  session was logged on far before he changed his password. Maybe this account tried to log on to the domain with an old ticked which caused the DC to lock his account. Maybe he had an open file which occasionally tried to save with old credentials. It did however solve the problem