≡ Menu

In this post, I will show you how to find list of Organization Units(OUs) in Active Directory that has protection enabled on them using Powershell

We know that, Active Directory now has built-in protection for Organization Units to prevent accidental deletions. This helps in unnecessary loss of data and saves the time that one need to spend on recovering the deleted data in case of such incidents.

We can determine if an OU has protection enabled or not by looking at the properties of it from ADUC (Active Directory Users and Computers). We can check this from PowerShell as well using the Get- Get-ADOrganizationalUnit cmdlet in ActiveDirectory module.

The Get-ADOrganizationalUnit cmd let by default returns all OUs in current Active Directory Domain. However, it returns only a set of properties not every attribute of that particular OU. This is by design of ActiveDirectory module provided by Microsoft. If you need additional Attributes, we need to use –Properties parameter. Look at the below examples.

Import-Module ActiveDirectory            
Get-ADOrganizationalUnit -Filter *            

Import-Module ActiveDirectory            
Get-ADOrganizationalUnit -Filter * -Properties *

Using this approach, we can query the value of ProtectedFromAccidentalDeletion property of each OU object which indicates the status of protection. If the value of it is $true then the OU has protection enable; otherwise no.

Now let us see how we can query the list of OUs that has protection enabled.

Import-Module ActiveDirectory            
Get-ADOrganizationalUnit -Filter * -Properties * | ? {$_.ProtectedFromAccidentalDeletion -eq $true} | Select Name

If you know the DistinguishedName of the Organization Unit, then you can query that directly to see if the protection is enabled.

Import-Module ActiveDirectory            
Get-ADOrganizationalUnit -Id "OU=US,DC=techibee,DC=com" -Properties * | Select ProtectedFromAccidentalDeletion

Similarly we can query by OU name as well.

Import-Module ActiveDirectory            
Get-ADOrganizationalUnit -LDAPFilter "(Name=US)" -Properties * | Select ProtectedFromAccidentalDeletion

If you are worried about the performance of the cmdlets, I would prefer you pass the required property names to –Properties attribute instead of *. That will make your queries faster.

 

{ 0 comments }

Change HP ILO User password using PowerShell

In this post, I will take you through a PowerShell script that helps in setting up the password for HP Integrated Lights-Out (ILO) console.

All these years, I was under kind of wrong impression that HP ILO can be managed via web console and a few utilities given by HP. Today I realized that it can also be managed using other protocols like SSH and Telnet. The moment I learned about it, I started thinking how can I leverage this and automation HP ILO user account change process.

After a bit of Googling, I came across a nice document from HP that gives all you need for managing HP ILO via SSH, telnet, web, and other HP utilities. You can download a copy of “HP iLO2 Scripting and Command line Guide” from HP website (click here).

I authored below quick script after going through the document. The script replies on a utility called plink(click here to download) which you should download and place in System path to make this script work. Using plink.exe, the script creates a SSH connection to HP ILO IP/Name. This utilities takes username and password required for the ILO connection and then changes the password of the account your specified to the key you want.

Code:

[cmdletbinding()]            
param(            
 [string]$ILOHostName,            
 [string]$ILOLoginUser,            
 [string]$ILOLoginUserpwd,            
 [String]$ILOUser,            
 [String]$ILOUserpwd,            
 [string]$PlinkPath            
)            

$ILOcmd = [String]::Format('{0} {1}@{2} -pw "{3}" "set /map1/accounts1/{4} password={5}" 2>&1',            
         $PlinkPath,            
         $ILOLoginUser,            
         $ILOHostName,            
         $ILOLoginUserpwd,            
         $ILOUser,            
         $ILOUserpwd)            

try {            
 [array]$Output = Invoke-Expression $ILOcmd -ErrorAction Stop            
 if($Output.Count -lt 2) {            
  Write-Host "Failed to set ILO password. `nLastExitCode : $LASTEXITCODE`nResult : $Output"            
  exit(1)            
 }            

} catch {            
 Write-Host "Failed to set ILO password. `nLastExitCode : $LASTEXITCODE`nResult : $Output"            
 exit(1)            
}            

if($LASTEXITCODE -eq 0 -and $Output[1].Split("=")[1] -eq 0) {            
 Write-Host "Password set successfully. `nLastExitCode : $LASTEXITCODE`nResult : $Output"            
} else {            
 Write-Host "Failed to set password. `nLastExitCode : $LASTEXITCODE`nResult : $Output"            
}

Usage:

In the below example, I am creating SSH connection to HP ILO of host1 with a user name called Administrator and setting password for user account myilouser1. Please note that you need to pass ILO name or IP address of target host to ILOHostName parameter.

.\Set-ILOUserPassword.ps1 -ILOHostName host1ILO -ILOLoginUser administrator -ILOLoginUserpwd mypass -ILOUser myilouser1 -ILOUserpwd user1pwd -PlinkPath c:\temp\plink.exe

This is a simple script I have written to demonstrate that HP ILO password can be set easily using PowerShell. I request you to test the script properly in test/lab environment before trying in production. You can use this script by modifying a bit to create users, delete users etc. Please refer to the aforementioned HP documentation for examples on how to do variety of actions using SSH connection.

Hope this helps.

{ 4 comments }

Change Drive letter of CD Drive using Powershell

In my previous post, I talked about how to query CD ROM Drive letter using PowerShell. In this post, I will show you how to change the drive letter of CD Drive in remote computers. This will come handy when you want to assign a specific drive letter to CD ROM drive.

In the below code I am using two WMI classes (1) Win32_CDROMDrive (2) Win32_Volume. The first one I am using to get current drive letter of CD drive in remote computer. I am using that drive letter and passing it to Win32_Volume WMI class as filter to get a instance of CD Drive and then using Set-WMIInstance cmdlet to set the drive letter of CD Drive to new Drive letter that is given as parameter to below function (i.e Set-CDROMDriveLetter function).

This code works find for computers that has one CD drive connected. The logic needs a bit of extension if your remote computer is having multiple CD drives. I haven’t made my code to support multiple cd drives at this moment, but if you have a need to do so, post it in comments section and I will try to provide help when I get some time. Otherwise, the code works great for computers with single CD drive installed.

The function takes two arguments. (1) ComputerName (2) NewDriveLetter. The ComputerName is an optional parameter and can take a single computer name or list of computer names. It can also read the computername from pipe line. If this parameter is not specified, the script executes against local computer. The second parameter, NewDriveLetter , is the Drive letter that you want to assign to your CD ROM. It should have a column after the drive letter character like G: as shown the below example.

Code:

function Set-CDROMDriveLetter {            
[cmdletbinding()]            
param(            
    [parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]            
    [string[]]$ComputerName = $env:COMPUTERNAME,            
    [parameter(Mandatory=$true)]            
    [string]$NewDriveLetter            
)            

begin {}            
process {            
    foreach($Computer in $COmputerName) {            
    $object = New-Object –TypeName PSObject –Prop(@{            
                'ComputerName'=$Computer.ToUpper();            
                'OldDriveLetter'= $null;            
                'NewDriveLetter'=$null;            
                'Status' = $null;            
               })            
    if(!(Test-Connection -ComputerName $Computer -Count 1 -ErrorAction 0)) {            
        Write-Verbose "$Computer is OFFLINE"            
        $object.Status = "OFFLINE"            
        $Object            
        Continue            
    }            
    try {            
        $cd = Get-WMIObject -Class Win32_CDROMDrive -ComputerName $Computer -ErrorAction Stop            
        $OldDriveLetter = $cd.drive            
        $cdvolume = Get-WmiObject -Class Win32_Volume -ComputerName $Computer -Filter "DriveLetter='$oldDriveLetter'" -ErrorAction Stop            
        Set-WmiInstance -InputObject $cdvolume -Arguments @{DriveLetter=$NewDriveLetter} -ErrorAction Stop | Out-Null            
        $Object.OldDriveLetter = $oldDriveLetter            
        $object.NewDriveLetter = $NewDriveLetter            
        $Object.Status = "Successful"            

    } catch {            
        Write-Verbose "Failed to Query WMI Class. $_"            
        $Object.Status = "Failed"            
        $Object            
        Continue;            
    }            

    $Object               

    }            
}            

end {}            

}

Output:

Hope this helps.. feedback welcome.

{ 0 comments }

Find drive letter of CD Drive using Powershell

Do you ever wanted to know if a remote computer is having CD drive attached to it? or drive letter of CD drive on remote computer? I got similar requirement today and came with below method after some research.

There is a WMI class named Win32_CDROMDrive in Windows which stores the details of CD ROM drive connected in a computer. This WMI class can give you variety of information about the connected CD Drive like status, Drive Letter, manufacturer, etc. In this post I will show you how to query drive letter and manufacturer details of remote computers.

Code:

function Get-CDROMDetails {                        
[cmdletbinding()]                        
param(                        
    [parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]                        
    [string[]]$ComputerName = $env:COMPUTERNAME                        
)                        
            
begin {}                        
process {                        
    foreach($Computer in $COmputerName) {                        
    $object = New-Object –TypeName PSObject –Prop(@{                        
                'ComputerName'=$Computer.ToUpper();                        
                'CDROMDrive'= $null;                        
                'Manufacturer'=$null                        
               })                        
    if(!(Test-Connection -ComputerName $Computer -Count 1 -ErrorAction 0)) {                        
        Write-Verbose "$Computer is OFFLINE"                        
    }                        
    try {                        
        $cd = Get-WMIObject -Class Win32_CDROMDrive -ComputerName $Computer -ErrorAction Stop                        
    } catch {                        
        Write-Verbose "Failed to Query WMI Class"                        
        Continue;                        
    }                        
            
    $Object.CDROMDrive = $cd.Drive                        
    $Object.Manufacturer = $cd.caption                        
    $Object                           
            
    }                        
}                        
            
end {}               
}

Usage and Output:

To get CDROM details of remote computers, try the below command.

PS:\>Get-CDROMDetails -ComputerName TIBPC1, TIBDC1

Hope this helps…

{ 1 comment }

While working on some PS stuff today related to active directory, I came across a need to verify if the given OU(Organization Unit) exists or not. Basically, I am taking OU as input to one of my scripts and would like to validate if that OU exists before doing any processing.  After a little bit of research, I found below is quick and useful.

function Test-ADOU {            
[cmdletbinding()]            
param(            
[string] $OuPath            
)            
try {            
    if([ADSI]::Exists("LDAP://$OuPath")) {            
        Write-Host "Given OU exists"            
    } else {            
        Write-Host "OU Not found"            
    }            
} catch {            
    Write-Host "Error Occurred while querying for the OU"            
}            
}

In the above code, the core part that checks for OU existence is [ADSI]::Exists() static method. It returns true if  OU exists, otherwise returns false. It will generate appropriate errors if it can not reach the domain you are querying.

Let us see a quick demo to see how it works. My AD structure is like below.

In the below screen first I queried for OldComps OU in the above structure and the the function returned saying “Given OU Exists”. I changed the input OU name to something that doesn’t exists and it returned the not found error message. In the third attempt, I changed the domain name to a non-existing domain.

Hope this helps…

 

{ 5 comments }

For troubleshooting weird issues, we need debug logging to be enabled at VM side in a Citrix XenDesktop VDI environment. This article explains how to enable this debug logging.

  1. Find WorkstationAgent.exe.config in C:\Program Files\Citrix\Virtual Desktop Agent folder and edit it
  2. Take a back of workstationagent.exe.config file. We can revert to this if something screwed up.
  3. Add the the LogFileName key as shown below.
  4.     <appSettings>
    <add key=”LogToCDF” value =”1″/>
            <add key=”LogFileName” value =”c:\temp\workstationlog.log”/>
    </appSettings>

  5. Save the file and restart Citrix Desktop Service

The file C:\temp\workstationlog.log file will be populated with all the debug information.

Hope this helps

{ 0 comments }

While troubleshooting a Citrix VDI agent failing to register with DDC(Desktop Delivery Controller), I came across a few interesting finds. I thought sharing this information on my blog will help many others.

The agent I was troubleshooting failing with below errors. After a few seconds, the agent is getting registered with DDC1 without any issues. But the moment user is trying to connect the VM6.local.com through VDI, the user is getting a standard error message similar to when no VMs available for establishing the connection.

Log Name:      Application
Source:        Citrix Desktop Service
Date:          3/1/2013 7:46:52 AM
Event ID:      1015
Task Category: None
Level:         Warning
Keywords:      Classic
User:          N/A
Computer:      VM6.local.com
Description:
The Citrix Desktop Service’s connection to the Citrix Desktop Delivery Controller Service was terminated. The Citrix Desktop Delivery Controller Service is running on server ‘DDC1’.

Check that there is no active firewall blocking the controller communicating with this machine.

Please refer to Citrix Knowledge Base article CTX117248 for further information.

Error details:
Keep-alive request was rejected.

After some troubleshooting I concluded that if there is a discrepancy in list of DDCs provided in web interfaces Vs the list of DDCs given to VDI agent at the time of installation, this error can occur. The reason is simple, VDI agent trusts the list of DDCs that are provided to it at the time of installation and registers to one of them. When it receives a broker connection request from a DDC which is not there is the ListofDDCs reg value( in side HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Citrix\VirtualDesktopAgent), the VDI agent denies the request and un registers from the already registered DDCs. It will try again after a minute or so to re-register again with DDC..

So, the lesson here is that, the list of DDCs given in Web Service for a given farm and the list available at HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Citrix\VirtualDesktopAgent\ListofDDCs keys should match. Otherwise, there is a possibility that you will run into issues.

Reference: http://support.citrix.com/article/CTX132536

Hope this helps…

{ 14 comments }

PsTip# Shutdown Computers remotely using Powershell

In one of my previous posts, I talked about how to shutdown a remote computer gracefully. It was useful and Powershell since it allows you to give comment etc. But the only problem is, we always don’t want to run big scripts for simple tasks. I need just a one liner when I want to shutdown the computer remotely. Of course we can use shutdown.exe but there is a powershell way too. The Stop-Computer can be used to accomplish this task. The advantage with using powershell cmdlet is, you can pipe computer names as input or provide multiple computer names as argument.

Here is a small example.

Stop-Computer -ComputerName PC1

This might fail if you have someone logged on to the computer. In such cases you will receive below error.

PS C:\> Stop-Computer -ComputerName PC1
Stop-Computer : This command cannot be run on target computer(‘PC1’) due to following error: The system shutdown
cannot be initiated because there are other users logged on to the computer.
At line:1 char:1
+ Stop-Computer -ComputerName PC1
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (PC1:String) [Stop-Computer], InvalidOperationException
+ FullyQualifiedErrorId : InvalidOperationException,Microsoft.PowerShell.Commands.StopComputerCommand

The solution in this is to use -Force switch to the above command.

Stop-Computer -ComputerName PC1 -Force

This should work in any case. Note that you have enough mechanism(like WOL) in place before you shutdown remote computers. Otherwise you have to go manually and swtich them ON 🙂

Hope this helps

{ 2 comments }

Powershell provides some sort of parallelism with RunSpaceFactory. It give good control over how many PS instances you want to create at any point of time, etc. It is very good(at the same time complex) and useful. Recently I came across another method in powershell which provides parallelism in a simple way. This is available only in Powershell V3. It has got a very good and large set of new features and one of them is WorkFlows(oh! I love it). When a Foreach is used inside workflow, it is allowing to given -Parallel option and it is doing the trick. That means, to utilize this parallelism you need to write a work flow. Don’t worry that is not too hard and we can write very simple and basic work flow to utilize this new feature.

Here I am giving a simple example on how to use this parallelism in Powershell V3. Here I am querying spooler service status on a list of remote computers using Workflow and I am get them the status in just a single shot.

Try the blow code in Powershell v3 and you will realize what I am saying. But the caveat here is, we can not control how many things it should execute in parallel. If you provide a array of 100 computers, then it will execute on all of them at time t — not sure how much time it will take to complete(I am still researching that).

WorkFlow Run-InParallel {            
param(            
[string[]]$ComputerName            
)            
    foreach -Parallel ($Computer in $ComputerName) {            
        $ser = Get-Service -PSComputerName $Computer -Name Spooler            
        "Status of {0} service on Computer {1} is {2}" -f $ser.Name, $Computer, $ser.Status            
    }            

}            

Run-InParallel -ComputerName PC1, PC2, PC3

Hope this tip Helps you. Feel free to comment if you have any suggestions or questions.

{ 4 comments }

In today PSTIP section, I will show you how to find empty folder/directories in a disk/folder using powershell. It is a simple scripts that takes directory or Drive name as parameter and searches for empty folders inside that path. Currently this script is not deleting any empty folders but you can update it to remove them if you want. Let me know if any one needs help in that.

[cmdletbinding()]            
param(            
 [Parameter(Mandatory=$true)]            
 [string]$DirectoryName            
)            

$Directories = Get-ChildItem -Path $DirectoryName -Recurse | ? {$_.PSIsContainer -eq $true }            
foreach($Directory in $Directories) {            
 $Items = Get-ChildItem -Path $Directory.FullName            
 if(!$items) {            
  Write-Host "$($Directory.Fullname) is empty"            
 }            
}

Usage:

.\Get-EmptyFolders.ps1 -DirectoryName C:\

Hope this helps. Feedback welcome.

{ 4 comments }