Friday, June 26, 2009

Clonando servidores a través de los discos.

Que tal,

Tal vez alguna vez han estado en una situación en la que se necesita "clonar servidores", pero no se tiene una herramienta como Altiris, Ghost, etc, si los servidores son del mismo modelo, mismos discos (velocidad, capacidad, etc) y misma tarjeta controladora PERC es posible hacer un clonado de los discos sin instalar nada y se tienen servidores identicos.

Esto lo acabo de hacer con servidores Dell con RAID 1, controladora PERC, 2 discos en espejo.

Aqui van los pasos:

1. Sacar del dominio (en caso de estar en alguno) al servidor, de preferencia tener instalado el OpenManage Server Administrator.
2. Apagar el servidor y sacarle uno de los discos.
3. Al nuevo servidor apagarlo tambien y sacarle uno de los discos, de preferencia del mismo lado.
4. Encender el equipo y entrar a la controladora de la PERC (Ctrl+M), navegar en la configuracion hasta la parte donde vienen los discos duros y marcar el disco original (no el que le acabamos de poner) como offline, lo que hará esto, es marcar el disco como degradado y ya no lo tomará en cuenta para bootear ni nada.
5. Reiniciar el equipo, al iniciar entrar al Server Administrator, navegar hasta el disco marcado como degradado y forzar un rebuild, con esto se empezará a reconstruir el disco que ya tenia el servidor con la nueva información.
6. Ahora, con el disco que habiamos sacado del servidor, meterselo de nuevo al servidor y hacer el mismo procedimiento, encender el servidor, entrar a la controladora, marcar el disco que acabamos de poner como degragado y encender el equipo.
7. Entrar al OpenManage Server Administrator y forzar un rebuild.


Con esto se tienen 2 servidores iguales, sin hacer nada de configuracion, puede sonar un poco complicado y riesgoso, pero es muy efectivo.

Friday, June 19, 2009

Getting the file share resources properties from a Windows 2003 cluster.

Recently I was asked to do a script that generate a script to recreate all the file share resources from a Windows 2003.

I'm sharing the script I did, I'm using PowerShell, to run it needs the cluster name to connect to, then via WMI will get all the shares and using the tool cluster.exe (Available if you have the Administrative Tools) will get all the resource properties.

I run it every day in all my clusters, it creates a folder with the day it run, so you can run it everyday and have all the historical changes.


# Process to generate the script to re-create the file share resources from a Windows 2003 cluster.
# It needs the cluster name as a parameter and it generates the script with the configuration from each resource.
# The new cluster name will be NEWCLUSTER so you need to change it before run it.

param([string]$ServerOp) # Cluster Server Name.
$mydate = get-date -uformat "%Y%m%d"
$FolderScripts = "d:\DRCluster\scripts\$mydate\" # It will create this folder, so we have a folder by date.
$FileLog="d:\DRCluster\logs\DrCluster_$mydate.log" # Log File Name

clear-host
if (!$ServerOp){ throw "You must specify the cluster name" }

$f = [System.IO.Path]::Combine($FolderScripts, $ServerOp + "_shares.txt")
[string]$hora=get-date -uformat "[%r]"
out-file -filepath $FileLog -inputobject "#############################################################################################################################################" -append
out-file -filepath $FileLog -inputobject " " -append
out-file -filepath $FileLog -inputobject " PROCESS TO GENERATE THE SCRIPT TO RECREATE THE CLUSTER: $hora " -append
out-file -filepath $FileLog -inputobject " " -append
out-file -filepath $FileLog -inputobject "#############################################################################################################################################" -append
out-file -filepath $FileLog -inputobject " " -append

[string]$hora=get-date -uformat "[%r]"
if (!(Test-Path $FolderScripts)) {
out-file -filepath $FileLog -inputobject "$hora The folder $FolderScripts is created" -append
md $FolderScripts | Out-Null
if($? -ne $True) {
out-file -filepath $FileLog -inputobject "$hora ERROR: Couldn't create the folder: $FolderScripts" -append
throw "The folder $FolderScripts could not be created"
}
}

$f = [System.IO.Path]::Combine($FolderScripts, $ServerOp + "_shares.txt")
out-file -filePath $f -inputobject "REM ######################################################################################################## " -Encoding ASCII
out-file -filePath $f -inputobject "REM SCRIPT TO RECREATE THE SHARES IN ANOTHER CLUSTER" -Append -Encoding ASCII
out-file -filePath $f -inputobject "REM VERIFY DRIVES AND CLUSTER NAME BEFORE RUN IT! " -Append -Encoding ASCII
out-file -filePath $f -inputobject "REM ######################################################################################################## " -Append -Encoding ASCII
out-file -filePath $f -inputobject " " -Append -Encoding ASCII
out-file -filePath $f -inputobject " " -Append -Encoding ASCII

[string]$hora=get-date -uformat "[%r]"
out-file -filepath $FileLog -inputobject "$hora Getting the shares..." -append
$shares = Get-WMIObject Win32_Share -Computer $ServerOp
if($? -ne $True) {
out-file -filepath $FileLog -inputobject "$hora ERROR: There was an error getting the shares" -append
break
}else{
[string]$hora=get-date -uformat "[%r]"
out-file -filepath $FileLog -inputobject "$hora Done.." -append
}
out-file -filepath $FileLog -inputobject " " -append
[string]$hora=get-date -uformat "[%r]"
out-file -filepath $FileLog -inputobject "$hora Begin processing every share to get the configuration" -append
foreach ($sharefolder in $shares){
if (0 -eq $sharefolder.Type){

$Groups = Get-WmiObject -class MSCluster_ResourceGroupToResource -namespace "root\mscluster" -computername $ServerOp
$name=$sharefolder.Name

$Group = $Groups | Where-Object { $_.PartComponent -eq "MSCluster_Resource.Name="""+$name+"""" }
if ($Group -ne $null){
[string]$mygrp = $Group.GroupComponent
$Groupshare = $mygrp.Substring($mygrp.IndexOf(""""),$mygrp.Length-$mygrp.IndexOf(""""))
[string]$mystring="cluster /cluster:NEWCLUSTER resource "+"""$name"""+ " /create /group:"+$Groupshare+ " /type:""File Share"""
out-file -filepath $f -inputobject $mystring -append -Encoding ASCII
[string]$mystring="cluster /cluster:NEWCLUSTER resource "+"""$name"""+ " /priv path="""+$sharefolder.Path+""""
out-file -filepath $f -inputobject $mystring -append -Encoding ASCII
[string]$mystring="cluster /cluster:NEWCLUSTER resource "+"""$name"""+ " /priv Sharename="+$sharefolder.name
out-file -filepath $f -inputobject $mystring -append -Encoding ASCII
[string]$mystring="cluster /cluster:NEWCLUSTER resource "+"""$name"""+ " /priv Remark="""+$sharefolder.Description+""""
out-file -filepath $f -inputobject $mystring -append -Encoding ASCII
[string]$mystring="cluster /cluster:NEWCLUSTER resource "+"""$name"""+ " /prop Description="""+$sharefolder.Description+""""
out-file -filepath $f -inputobject $mystring -append -Encoding ASCII

$Access = @();
$SD = (Get-WMIObject -Class Win32_LogicalShareSecuritySetting -Computer $ServerOp -Filter "Name='$($name)'").GetSecurityDescriptor().Descriptor
$SD.DACL | %{ $Trustee = $_.Trustee.Name
If ($_.Trustee.Domain -ne $Null) { # The account has a domain.
$Trustee = "$($_.Trustee.Domain)\$Trustee"
$Access+= New-Object System.Security.AccessControl.FileSystemAccessRule($Trustee, $_.AccessMask, $_.AceType)
} elseif ( $_.Trustee.Name -ne $Null ) { # The account doesn't have a domain, but still valid, e.g. Everyone
$Access+= New-Object System.Security.AccessControl.FileSystemAccessRule($Trustee, $_.AccessMask, $_.AceType)
}else{ # The account doesn't have a domain nor valid.
Write-Host "Warning: Found an account in the share that could not be resolved, its possible that doesnt exists in AD"
$sharetemp1 = $sharefolder.Name
out-file -filepath $FileLog -inputobject "Found an account in the share that could not be resolved, its possible that doesnt exists in AD" -append
}
}
foreach($permission in $Access){
if ( $permission.FileSystemRights -eq "ReadAndExecute,Synchronize"){ $perm = "R" }
elseif ( $permission.FileSystemRights -eq "Modify,Synchronize"){ $perm ="RC" }
elseif ( $permission.FileSystemRights -eq "FullControl") { $perm = "F" }
if ($permission.AccessControlType -eq "Allow") {
[string]$mystring="cluster /cluster:NEWCLUSTER resource "+"""$name"""+ " /priv Security="""+$permission.IdentityReference+""""+",grant,"+$perm+":security"
out-file -filepath $f -inputobject $mystring -append -Encoding ASCII
}
}
$cmd = cluster.exe $ServerOp res $name /listdep | select-string "Online"
if($? -ne $True) {
out-file -filepath $FileLog -inputobject "$hora ERROR: THERE WAS AN ERROR GETTING THE DEPENDENCIES" -append
}
$gtp = " " + $Groupshare.substring(1,$Groupshare.length-2) + " "
foreach ($myline in $cmd){
[string]$valor=$myline;
$dep=($valor.substring(0,$valor.indexOf($gtp,$gtp.length-1))).trimend()
[string]$mystring="cluster /cluster:NEWCLUSTER resource "+"""$name"""+ " /AddDep:"""+$dep+""""
out-file -filepath $f -inputobject $mystring -append -Encoding ASCII
}

[string]$mystring="cluster /cluster:NEWCLUSTER resource "+"""$name"""+ " /On"
out-file -filepath $f -inputobject $mystring -append -Encoding ASCII
out-file -filepath $f -inputobject " " -append -Encoding ASCII
}
}
}
[string]$hora=get-date -uformat "[%r]"
out-file -filepath $FileLog -inputobject "$hora Done getting the shares properties" -append
out-file -filePath $FileLog -inputobject " " -Append

out-file -filepath $FileLog -inputobject "#############################################################################################################################################" -append
out-file -filepath $FileLog -inputobject " " -append
out-file -filepath $FileLog -inputobject " END OF THE PROCESS: $hora " -append
out-file -filepath $FileLog -inputobject " " -append
out-file -filepath $FileLog -inputobject "#############################################################################################################################################" -append



This will generate a file that will look like this:
cluster /cluster:NEWCLUSTER resource "Backup" /create /group:"mygroup" /type:"File Share"
cluster /cluster:NEWCLUSTER resource "Backup" /priv path="t:\mypath"
cluster /cluster:NEWCLUSTER resource "Backup" /priv Sharename=MYSHARE
cluster /cluster:NEWCLUSTER resource "Backup" /priv Remark="comments of my share"
cluster /cluster:NEWCLUSTER resource "Backup" /prop Description="comments of my share"
cluster /cluster:NEWCLUSTER resource "Backup" /priv Security="BUILTIN\Administrators",grant,F:security
cluster /cluster:NEWCLUSTER resource "Backup" /priv Security="MYDOMAIN\MYGROUP",grant,F:security
cluster /cluster:NEWCLUSTER resource "Backup" /priv Security="MYDOMAIN\MYACCOUNT",grant,F:security

Revisar permisos directos en SQL.

Es muy frecuente que se establezca como política (o como un Best Practice) que los permisos a los objetos de una base de datos se den solamente a roles y no a usuarios.

Esto es muy importante, es mucho mas recomendable asignar permisos a través de roles que a usuarios, de tal manera, que en caso de migrar una base de datos a un servidor diferente, solamente se tengan que recrear los usuarios, y no recrear los usuarios y tener que volver a asignar permisos.

Les paso un script que utilizo para detectar si se tienen permisos directos a los usuarios de una base de datos, en caso de ser así, se listan los objetos y los permisos, yo lo utiliza dentro de un script de PowerShell que me indica en caso de obtener algun registro que se asignaron mal los permisos.

El script recorre todas las bases de datos y revisa si asignaron permisos directos.

Espero les sirva:


SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[BPSQL_Revisa_Permisos_Users]
AS

DECLARE @dbname2 varchar(40);
declare @id varchar(30);

DECLARE dbname CURSOR FOR select name,database_id from master.sys.databases;

CREATE TABLE #Permisos_Users(
[base_de_datos] [varchar](128) NULL,
[usuario] [varchar](128) NULL,
[tipo_permiso] [varchar](128) NULL,
[objeto] [varchar](128) NULL
)

OPEN dbname
FETCH NEXT FROM dbname
INTO @dbname2,@id

WHILE @@FETCH_STATUS = 0
BEGIN
exec('SET NOCOUNT ON;
use [' + @dbname2 + ']
INSERT #Permisos_Users
select d.name,b.name,a.permission_name,c.name from sys.database_permissions a inner join sys.database_principals b on a.grantee_principal_id=b.principal_id
inner join sys.objects c on a.major_id=c.object_id,sys.databases d where b.type_desc in (''SQL_USER'',''WINDOWS_USER'',''WINDOWS_GROUP'') and b.default_schema_name is not null
and b.name <> ''dbo'' and b.name <> ''guest'' and a.permission_name <> ''CONNECT'' and convert(varchar,d.database_id)='+@id+'')

FETCH NEXT FROM dbname INTO @dbname2,@id
END

CLOSE dbname
DEALLOCATE dbname

select * from #Permisos_Users

drop table #Permisos_Users