All posts by steven

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

Problem with Zen Photo

A webhosting client reported that he wasn’t able to connect to the admin area of Zen Photo.  My web service uses Plesk. His previous webhosting was under Cpanel. I first uploaded the setup files of his version of Zen Photo. Then I asked him to perform the update. This failed by an internal server error (500). The first thing I did at that point was looking through the logfiles. The log contain the following errors

mod_fcgid: stderr: PHP Warning: file_exists(): open_basedir restriction in effect. File(/var/lib/php5) is not within the allowed path(s):

To resolve this I added the path /var/lib/php5 to the allowed list. This is a slight security risk. Most binaries are located in the allowed list anyways so I added the configuration like below:

Screen Shot 2015-04-04 at 16.34.17

After this configuration change the users was able to connect to the admin area and also capable of upgrading Zen photo to the last stable built.

However quite soon after that the user started to report another problem. When he was browsing through the admin area he experienced internal server errors again. I started to look at the log files for that user and the following error was reported:

[core:error] [pid 5159] [client x.x.x.x:49520] End of script output before headers: admin-options.php, referer: 
[fcgid:warn] [pid 5159] (104)Connection reset by peer: [client x.x.x.x:49520] mod_fcgid: error reading data from FastCGI server, referer

The log reported this error from many different reference scripts. Mostly any reference script. By then I tried many things from disabling the XSS protection. Disabling HSTS, Disabling PHP modules and changing the file and folder permissions. I started looking at the error log of apache2 server located in /var/log/apache2/error.log. The following error caught my eye:

[fcgid:error] [pid 1886] mod_fcgid: process /var/www/cgi-bin/cgi_wrapper/cgi_wrapper(7917) exit(communication error), get unexpected signal 11
[fcgid:error] [pid 1886] mod_fcgid: process /var/www/cgi-bin/cgi_wrapper/cgi_wrapper(8193) exit(communication error), get unexpected signal 11

The first thing I thought was that the account of that user did not have enough permissions over the wrapper script. But that was not the case. So at that point I was able to rule out the web server and any file permissions. I somehow got the idea that this error must have had something to do with the difference in it’s PHP version between mine server at the one of the previous hoster. So I had an older server with PHP 5.3 installed. I migrated the account of that customer to that older server and let him recheck. The user reported no issue at all. PHP 5.3 uses xcache and PHP 5.5 uses the newer opcache as Zend extension. So what I did was changing an PHP configuration file (/etc/php5/apache2/conf.d/05-opcache.ini) from this:

; configuration for php ZendOpcache module
; priority=05
zend_extension=opcache.so

into this

; configuration for php ZendOpcache module
; priority=05
#zend_extension=opcache.so

I restarted the web server and have the customer look again. He did not find any issues. Of course this causes many issues on the server. I quickly reverted the file to the previous version.

Ultimately resolving this issue I did the following to his PHP configuration:

Screen Shot 2015-04-04 at 16.53.12

 

opcache.enable=0

 

I am so happy with Plesk and all it does to make my life easier.

GCI the funny way

Most recently I received a request from a user that wanted to know what folders are being used and what folders are not. We use Datadvantage for that so I sent him an export of the folders. But there is another way of doing that. You can do a GCI and count the number of slashes (\). You need to count the slashes to know at what level you need to do a Get-ChildItem -recurse. I exported a 3 level export as described in this post. Then at the last level I want to do a CGI  -recurse.

In my case it where 9 slashes. This differs per environment. You can count it by entering the following code at the last level

$Folder = "\\Folder\Path"
[string]$Test = $Folder
[int]$Result = $Test.Split("\").Count
Write-Host The number of levels to split on is $Result

If you know when to do a recursive CGI you can change the script below.

$path = "H:\Exports\ExportCGI.csv"
$list = import-csv $path
$Date = (Get-Date).AddDays(-400)
Foreach ($item in $list){[string]$Test = $item.folders
[int]$Result = $Test.split("\").count
If ($Result -lt "9"){$Result2 =@( gci $Item.Folders | Where {$_.LastWriteTime -gt $Date}); $Result2 = $Result2 | Sort LastWriteTime -Descending ; $OutInfo = $Item.Folders + ";" + $Result2[0].LastWriteTime ; AC -Value $Outinfo -Path "H:\Exports\ExportCGIPlusLastWriteTime.csv"}
If ($Result -ge "9"){$Result2 = @(gci $Item.Folders -Recurse | Where {$_.LastWriteTime -gt $Date}); $Result2 = $Result2 | Sort LastWriteTime -Descending ; $OutInfo = $Item.Folders + ";" + $Result2[0].LastWriteTime ; AC -Value $Outinfo -Path "H:\Exports\ExportCGIPlusLastWriteTime.csv"}
}

In My case I added a condition to only include the date if there was a file or folder that has been modified in the last 400 days. You may alter this in the way you want.

Have fun!

Monitor members of the Administrative Group

In a domain with multiple Administrators a rogue Administrator case should be avoided. Some Administrators want to built a backdoor into the domain for whatever reason. Managing these backdoors should be a priority. I have written a script that queries members of the Administrative groups that you might want to monitor. The script than compares it with a reference list that was previously created. If an Administrator is added or removed from one of the group you’ll be notified by mail. To make this work you need to schedule this Powershell script. You’ll also need to have relay permission to a SMTP server from where you run the script. In my case I added a schedule of once an hour. The script expects a file called “Groepenlijst” in which you add the groups.

Import-Module ActiveDirectory
$Path = "LocationOfFile"
$Groups = GCI $Path | Where {$_.FullName -match "Groepenlijst"} | Get-Content
$MailServer = "Mailserver"
$Recipients = "Recipient@Domain.TLD","Recipient@Domain.TLD","Recipient@Domain.TLD"

Foreach ($Group in $Groups){
$Result = GCI $Path | Where {$_.FullName -match $Group}
If ($Result -eq $null){$Members = Get-ADGroupMember $Group -recursive | Select SamAccountName
$File = ($Path+"\"+$Group+".csv")
$Members | Export-CSV -Path $File -Delimiter ";" -NoTypeInformation
}}

Foreach ($Group in $Groups){
$Result = GCI $Path | Where {$_.FullName -match $Group}
$DifferenceList = Import-CSV $Result.FullName
$ReferenceList =  Get-ADGroupMember $Group -recursive | Select SamAccountName
$MembersRemoved = @()
$MembersAdded = @()
$CompareResult =@( Compare -Property SamAccountName -ReferenceObject $ReferenceList -DifferenceObject $DifferenceList)

Foreach ($item in $CompareResult){
If ($item.sideindicator -eq "<="){$MembersAdded += $Item}
If ($item.sideindicator -eq "=>"){$MembersRemoved += $Item}
}

If ($MembersAdded.count -ne 0){
If ($Body -ne $null){
If ($MembersAdded.Count -gt 1){$Body = $Body +"`n`n" + "Er zijn nieuwe members toegevoegd aan de groep: "+ $Group + ". De members zijn:" + "`n`n"
Foreach ($Item in $MembersAdded){$Body = $Body + $item.samaccountName + "`n"}}
If ($MembersAdded.Count -eq 1){$Body = $Body + "`n`n" + "Er is een nieuwe member toegevoegd aan de groep: "+ $Group + ". De member is:" + "`n`n" + $MembersAdded.samaccountName}}
If ($Body -eq $null){
If ($MembersAdded.Count -gt 1){$Body = "Er zijn nieuwe members toegevoegd aan de groep: "+ $Group + ". De members zijn:" + "`n`n"
Foreach ($Item in $MembersAdded){$Body = $Body + $item.samaccountname + "`n"}}
If ($MembersAdded.Count -eq 1){$Body = "Er is een nieuwe member toegevoegd aan groep: "+ $Group + ". De member is:" + "`n`n" + $MembersAdded.samaccountname}}
}
If ($MembersRemoved.count -ne 0){
If ($Body -ne $null){
If ($MembersRemoved.Count -gt 1){$Body = $Body +"`n`n" + "Er zijn members verwijderd uit de groep: "+ $Group + ". De members zijn:" + "`n`n"
Foreach ($Item in $MembersRemoved){$Body = $Body + $item.samaccountName + "`n"}}
If ($MembersRemoved.Count -eq 1){$Body = $Body + "`n`n" + "Er is een member verwijderd uit de groep: "+ $Group + ". De member is:" + "`n`n" + $MembersRemoved.samaccountName}}
If ($Body -eq $null){
If ($MembersRemoved.Count -gt 1){$Body = "Er zijn members verwijderd uit de groep: "+ $Group + ". De members zijn:" + "`n`n"
Foreach ($Item in $MembersRemoved){if ($item.sideindicator -eq "=>"){$Body = $Body + $item.samaccountname + "`n"}}}
If ($MembersRemoved.Count -eq 1){$Body = "Er is een member verwijderd uit de groep: "+ $Group + ". De member is:" + "`n`n" + $MembersRemoved.samaccountname}}
}}

If ($Body -ne $null){
Send-MailMessage -From "FromAddress@Domain.TLD" -Subject "Nieuwe of verwijderde Member(s) voor administratieve groepen" -To $Recipients  -Body $Body -SmtpServer $MailServer
}

Foreach ($Group in $Groups){
$Members = Get-ADGroupMember $Group -recursive | Select SamAccountName
$File = ($Path+"\"+$Group+".csv")
$Members | Export-CSV -Path $File -Delimiter ";" -NoTypeInformation -Force
}

In my “groepenlijst” I have added the following groups:

Domain Admins
Enterprise Admins
Schema Admins
Administrators
Backup Operators
Organization Management
Server Operators
Server Management

Adjust this at will.

 

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

App-v 4.6 large cache detection and alert script

App-v 4.6 generates sometime a large sftfs.fsd file. Users are experiencing performance issues when the file exceeds 15 GB. To fix this problem administrators remove the file by rebooting the Xenapp in safe mode. No user should be logged in of course. The Citrix servers therefore need to be empty. My colleagues ran a VB script that did the job of detecting but nothing else. I have made a script in Powershell that does the same but also set the server in ProhibitLogOns logon mode. The implement this script the server must have the Xenapp SDK installed. The server which runs the script need to have relay rights on the SMTP server. You’ll also need to have a Xenapp Server with PSRemoting enabled or the server itself should be member of the Xenapp farm.

#Load Modules and Snapins
Import-Module ActiveDirectory
Add-PSSnapin Citrix*
#Opens array for servers that apply on the conditions specified below
$ProhibitLogonServers =@()
$SMTP = "SMTPSERVER"
#Get all Xenapp server based on OU
$list = Get-ADComputer -Filter * -SearchBase "OU=Citrix Servers,OU=Servers,DC=Domain,DC=LOCAL"
Foreach ($Computer in $List){$Server = $Computer.DNSHostName 
$Result = GI "\\$Server\d$\AppVClientcache\Global\SoftGrid Client\sftfs.fsd"
If ($result.length -ne 0){$Outinfo = $Server+";"+$Result.length+" Bytes"
AC -Value $Outinfo -Path ".\log.csv"
#The conditions are met if the file is greater and engels 15 GB
If (($Result.length / 1073741824 ) -ge 15){$ProhibitLogonServers += $Computer}}}

#Mail message if no server are found                                                                                                                                                                            
If ($ProhibitLogonServers.length -eq 0){
$Mail = "Er is geen server gevonden die voldoet aan de voorwaarden om op ProhibitLogons te worden gezet" + "`n`n" + "Met Vriendelijke groet," +  "`n" + "Ochtend Controle Script"
Send-MailMessage -From "SendingAddress" -Subject "Prohibitlogons Report" -To "DestinationAddress" -Body $mail -SmtpServer $SMTP}

#Mail message one server is found
If ($ProhibitLogonServers.count -eq 1){
$Mail = "Er is een server gevonden die voldoet aan de voorwaarden om op ProhibitLogons te worden gezet. De server heeft als naam:" + "`n`n" + "$ProhibitlogonServers.Name" +"`n`n"+ "Met vriendelijke groet," + "`n`n" + "Ochtendcontrole Script"
Send-MailMessage -From "SendingAddress" -Subject "Prohibitlogons Report" -To "DestinationAddress" -Body $mail -SmtpServer $SMTP}

#Mail message if more than one server is found
If ($ProhibitLogonServers.count -gt 1){
$Mail = "Er zijn servers gevonden die voldoen aan de voorwaarden om op ProhibitLogons te worden gezet. De servers hebben de naam:" + "`n`n"
foreach ($server in $prohibitlogonservers){$Mail = $Mail + $Server.Name + "`r`n"}
$Mail = $Mail  + "`n" + "Met vriendelijke groet," + "`n`n" + "Ochtend Controle Script"
Send-MailMessage -From "SendingAddress" -Subject "Prohibitlogons Report" -To "DestinationAddress" -Body $mail -SmtpServer $SMTP}

#Opens array to check if the server has been set to prohibit logons. If servers are found a retry will be executed
$FailedServers = @()
If ($Prohibitlogonservers -ne $null){
Foreach ($Server in $Prohibitlogonservers){Set-XAServerLogOnMode -ServerName $Server.Name -LogOnMode Prohibitlogons -ComputerName CitrixServer}
Foreach ($Server in $Prohibitlogonservers){If ((Get-XAServer -ServerName $Server.Name -ComputerName CitrixServer).LogonMode -notmatch "ProhibitLogonOns"){$FailedServers += $Server}}}

#If a server is rebooting during the process you do want to make sure that server is set to Prohibit Logons. 
Start-sleep -s 900

#Retry setting the server to prohibit logons
If ($Failedservers.count -ne 0){
Foreach ($Server in $FailedServers){Set-XAServerLogOnMode -ServerName $Server.Name -LogOnMode Prohibitlogons -ComputerName CitrixServer}
$Failedservers = @()
Foreach ($Server in $FailedServers){If ((Get-XAServer -ServerName $Server.Name -ComputerName CitrixServer).LogonMode -notmatch "ProhibitLogonOns"){$FailedServers += $Server}}}

#Mail message if servers aren't set to prohibit logons
If ($Failedservers.count -ne 0){$Mail = "Er zijn servers niet succesvol op prohibitlogons gezet. Deze servers zijn:" + "`n`n"
foreach ($server in $Failedservers){$Mail = $Mail + $Server.Name + "`r`n"}
$Mail = $Mail  + "`n" +'Zet deze server(s) handmatig op ProhibitLogons' + "`n`n" + "Met vriendelijke groet," + "`n`n" + "Ochtend Controle Script"
Send-MailMessage -From "SendingAddress" -Subject "Prohibitlogons Failed" -To "DestinationAddress" -Body $mail -SmtpServer $SMTP}

 

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

Multiple levels GCI

In Powershell version 3 there is no way to limit the number of levels to do something in directories/registry/AD and so forth. So if you want to do that you can use a script like this:

[int]$Levels = "4"
$Directory = "\\Directory"
$List = GCI $Directory | Where {$_.psiscontainer -eq $true}
$Templist = $list
$temp = @()
While ($levels -ne 1){
Foreach ($item in $Templist){
$Temp += gci $item.Fullname | Where {$_.PSisContainer -eq $True}}
$list += $Temp
$Templist = $Temp
$Levels--
$Temp =@()}
$list = $list | Sort-Object Fullname
Foreach ($line in $List){Add-content -value $Line.Fullname -path ".\Export.csv"}