vendredi 11 juin 2010

Infrastructure Master et Catalogue Global.....

Voici en substance ce que l'on trouve dans le livre Microsoft Press pour préparer l'examen 70-647, page 126:

Depuis l'apparition d'Active Directory avec Windows 2000 Server, les recommandations pour placer l'infrastructure master ont été assez confuses. Beaucoup de documentations disent qu'il ne faut pas mettre l'infrastructure master sur un serveur qui héberge le catalogue global, et d'autre disent que cela est possible si tout les contrôleurs de domaines héberge ce fameux catalogue...

Ce n'est pas forcément faux, pas complètement vrai, mais franchement incomplet:

L'infrastructure master est un rôle qui existe dans chaque domaine d'une forêt. Son job est de comparer les objets du domaine auquel il appartient avec les objets des autres domaines de la forêt, et de renseigner le catalogue global.
Le catalogue global détient une copie partielle (et indexée) de chaque objets de la forêt (entre autre l'appartenance de chaque utilisateurs aux groupes universels, on en à grand besoin à l'ouverture de session), informations qui lui sont fournit par l'infrastructure master de chaque domaine de la forêt.

Si le serveur qui est infrastructure master héberge seul le catalogue global, il ne verra aucune différence. L'infrastructure master ne fera donc aucun changement dans le catalogue global pour son propre domaine.

Si tout les contrôleurs de domaine dans une forêt (le texte original parle de domaine, mais je pense que c'est une erreur) héberge le catalogue global, l'infrastructure master n'aura rien à faire, car le catalogue global aura déjà toutes informations sur les objets des autres domaines.

Pour résumé, si vous avez une forêt avec un domaine unique, l'infrastructure master n'a rien à comparer, puisqu'il n'y a pas d'autre domaines. Il peut donc héberger le catalogue global.

Si votre forêt contient plusieurs domaines, et que TOUS les contrôleurs de domaines de votre forêt héberge le catalogue global (faîtes attention à la réplication, pensez à votre bande passante!!!!), le catalogue global possède déjà toutes les informations qui lui sont nécessaire, l'infrastructure master n'a pas besoin de le renseigner, donc l'infrastructure master peut résider sur un serveur de catalogue global.

Dans les autres cas, l'infrastructure master ne doit pas héberger le catalogue global, sinon celui sera incomplet, ce qui aura pour conséquence, entre autre, des temps d'ouverture de session particulièrement long pour vos utilisateurs

Et voila ;-)

Préparer une forêt 2003 Server pour 2008 Server

Le support de Windows 2003 Server devant s'arrêter prochainement, en 2012 je croix, il faut penser à mettre à jour nos infrastructures Active Directory sous 2008 Server.

Pour cela, il faut préparer le schéma de votre forêt AVANT d'ajouter des contrôleurs de domaine 2008 Server. Tout d'abord, vous ne devez plus avoir de contrôleurs de domaines NT4, et le niveau de fonctionnalité de votre forêt doit être au minimum Windows 2000 Native si vous avez encore des DC 2000 Server. Si c'est le cas, vos DC 2000 doivent être en SP4 avec le hotfix 265089 installé.
Si vous n'avez que des contrôleurs de domaines 2003 Server, le niveau de fonctionnalité de votre forêt doit être Windows 2003 Server, et non pas Windows 2003 Server Interim ou intermédiaire.
Récupérer le répertoire adprep dans le répertoire support du DVD d'installation 2008 Server. Il contient deux exécutable, adprep et adprep32. L'un pour les archi 64 bits (adprep), et adprep32, je vous laisse deviner.... ;-)
Repérer le schema master de votre infrastructure http://support.microsoft.com/kb/234790
(ou alors, plus rapide, un petit netdom query fsmo)
et exécuter les commandes suivantes en tant qu'administrateur membre des groupe administrateur du schéma, administrateur de l'entreprise, administrateurs du domaine sur le contrôleur de domaine qui détient le rôle de schema master:

adprep /forestPrep

et dans la foulé, lorsque /forestPrep est terminé, sur le schema master aussi:

adprep /rodcPrep

Une fois ceci terminé, vous n'avez plus qu'a repérer l'infrastructure master de chacun des domaines dans lesquels vous voulez intégrer des DC 2008 Server, et lancer la commande suivante:

adprep /domainprep /gpprep

Et voila, votre domaine et prêt pour acceuillir des DC 2008 Server ainsi que des RODC.

Et voilà ;-)

vendredi 21 novembre 2008

Hello world!

Bonjour à tous,

Un p'tit blog sans prétention ou je vais essayer de vous part de ce que je peux découvrir au cours de mon travail. Je suis informaticien, du moins j'essaye, ce domaine étant très vaste et varié. Je suis spécialisé en administration système et réseau, donc pas du tout développeur. Si certains d'entre vous trouve des aneries dans ce que je raconte, n'hésitez pas à me le faire savoir, preuve à l'appui bien sûr ;-).
J'ai une préférence pour les Unix, MacOSX, FreeBSD, OpenBSD et Linux. Cela ne m'empêche pas de suivre les cours Microsoft, car cet éditeur est incontestablement très présent sur le marché, et en recherche constante d'amélioration de ses produits. Certains critiqueront la politique tarifaire, mais passer du temps pour essayer de faire la même chose en moins bien avec des produits libres n'est pas forcément économique non plus. Donc intégriste de tel ou tel technologie, passez votre chemin, mon credo étant d'utiliser l'outil le mieux adapté pour faire mon boulot, le plus efficacement possible, en faisant abstraction de mes préférences. Surtout que ce qui fait que souvent on préfère une technologie à une autre, c'est la méconnaissance (pour ne pas dire incompétence) des produits.

On va commencer par parler des sauvegardes des données et paramètres utilisateurs sous Windows, à partir de XP et ultérieur. Il faut savoir qu'il n'est pas simple de procéder à une sauvegarde de fichiers, car certains d'entre eux sont verrouillés par le système. Pas moyens de les copier comme ça, comme on le ferait sous Unix. Pour y accéder, il faut passer par le Volume Shadow copy Service, gentiment appelé VSS par Microsoft. Quand on veut développer sa propre appli, il faut passer par le VSS SDK qui va vous apporter tout ce qu'il faut y parvenir.
Le but du VSS est de prendre des clichés instantanés, des snapshots, du ou des disques dur. En fait, c'est une image du contenu du disque dur (ou volumes pour être un peu tatillon) accessible en lecture seule.

Pour faire simple, le VSS est composé de trois entités:
  1. Les providers:
  2. Les requesters:
  3. Les writers:
Les providers gèrent les volumes courants et créent les snapshot à la demande. On a de la chance, Microsoft en fourni par défaut qui iront bien pour ce qu'on a à faire.

Les requesters sont typiquement des appli de sauvegarde/restauration, comme ntbackup. Ce sont eux qui vont demander aux provider de fournir un ou plusieurs snapshot afin d'avoir une sauvegarde cohérente. Optionnellement, les requesters coordonnent leurs efforts avec les writers présents sur le système. Si il ne le font pas, il semble qu'on ne peut pas restaurer par le VSS. On se retrouve avec un état dit "crash consistent", c'est à dire le même état que si vous éteignez sauvagement votre machine. C'est donc ce type de code que l'on va s'efforcer d'écrire.

Et enfin, les writers. Un writer doit être implémenter par une appli qui veux s'assurer de fournir un état cohérent sur le snapshot. Typiquement un SGBDR par exemple (SQL server implémente un writer). Si on veut pouvoir restaurer, à ce que j'ai pu comprendre de la documentation (très obscure pour moi) sur msdn, il va falloir en écrire un. Au moment ou j'écris ces lignes, je n'y suis toujours pas parvenu.

Voila le code source.

Tout d'abord, debug.cpp:
#include "stdafx.h"
#include "vss.h"
#include "vswriter.h"
#include "vsbackup.h"
#include
//#include
#include
#include


LPCWSTR GetStringFromFailureType(HRESULT hrStatus)
{
LPCWSTR pwszFailureType = L"";

switch (hrStatus)
{
case VSS_E_WRITERERROR_INCONSISTENTSNAPSHOT: pwszFailureType = L"VSS_E_WRITERERROR_INCONSISTENTSNAPSHOT"; break;
case VSS_E_WRITERERROR_OUTOFRESOURCES: pwszFailureType = L"VSS_E_WRITERERROR_OUTOFRESOURCES"; break;
case VSS_E_WRITERERROR_TIMEOUT: pwszFailureType = L"VSS_E_WRITERERROR_TIMEOUT"; break;
case VSS_E_WRITERERROR_NONRETRYABLE: pwszFailureType = L"VSS_E_WRITERERROR_NONRETRYABLE"; break;
case VSS_E_WRITERERROR_RETRYABLE: pwszFailureType = L"VSS_E_WRITERERROR_RETRYABLE"; break;
case VSS_E_BAD_STATE: pwszFailureType = L"VSS_E_BAD_STATE"; break;
case VSS_E_PROVIDER_ALREADY_REGISTERED: pwszFailureType = L"VSS_E_PROVIDER_ALREADY_REGISTERED"; break;
case VSS_E_PROVIDER_NOT_REGISTERED: pwszFailureType = L"VSS_E_PROVIDER_NOT_REGISTERED"; break;
case VSS_E_PROVIDER_VETO: pwszFailureType = L"VSS_E_PROVIDER_VETO"; break;
case VSS_E_PROVIDER_IN_USE: pwszFailureType = L"VSS_E_PROVIDER_IN_USE"; break;
case VSS_E_OBJECT_NOT_FOUND: pwszFailureType = L"VSS_E_OBJECT_NOT_FOUND"; break;
case VSS_S_ASYNC_PENDING: pwszFailureType = L"VSS_S_ASYNC_PENDING"; break;
case VSS_S_ASYNC_FINISHED: pwszFailureType = L"VSS_S_ASYNC_FINISHED"; break;
case VSS_S_ASYNC_CANCELLED: pwszFailureType = L"VSS_S_ASYNC_CANCELLED"; break;
case VSS_E_VOLUME_NOT_SUPPORTED: pwszFailureType = L"VSS_E_VOLUME_NOT_SUPPORTED"; break;
case VSS_E_VOLUME_NOT_SUPPORTED_BY_PROVIDER: pwszFailureType = L"VSS_E_VOLUME_NOT_SUPPORTED_BY_PROVIDER"; break;
case VSS_E_OBJECT_ALREADY_EXISTS: pwszFailureType = L"VSS_E_OBJECT_ALREADY_EXISTS"; break;
case VSS_E_UNEXPECTED_PROVIDER_ERROR: pwszFailureType = L"VSS_E_UNEXPECTED_PROVIDER_ERROR"; break;
case VSS_E_CORRUPT_XML_DOCUMENT: pwszFailureType = L"VSS_E_CORRUPT_XML_DOCUMENT"; break;
case VSS_E_INVALID_XML_DOCUMENT: pwszFailureType = L"VSS_E_INVALID_XML_DOCUMENT"; break;
case VSS_E_MAXIMUM_NUMBER_OF_VOLUMES_REACHED: pwszFailureType = L"VSS_E_MAXIMUM_NUMBER_OF_VOLUMES_REACHED"; break;
case VSS_E_FLUSH_WRITES_TIMEOUT: pwszFailureType = L"VSS_E_FLUSH_WRITES_TIMEOUT"; break;
case VSS_E_HOLD_WRITES_TIMEOUT: pwszFailureType = L"VSS_E_HOLD_WRITES_TIMEOUT"; break;
case VSS_E_UNEXPECTED_WRITER_ERROR: pwszFailureType = L"VSS_E_UNEXPECTED_WRITER_ERROR"; break;
case VSS_E_SNAPSHOT_SET_IN_PROGRESS: pwszFailureType = L"VSS_E_SNAPSHOT_SET_IN_PROGRESS"; break;
case VSS_E_MAXIMUM_NUMBER_OF_SNAPSHOTS_REACHED: pwszFailureType = L"VSS_E_MAXIMUM_NUMBER_OF_SNAPSHOTS_REACHED"; break;
case VSS_E_WRITER_INFRASTRUCTURE: pwszFailureType = L"VSS_E_WRITER_INFRASTRUCTURE"; break;
case VSS_E_WRITER_NOT_RESPONDING: pwszFailureType = L"VSS_E_WRITER_NOT_RESPONDING"; break;
case VSS_E_WRITER_ALREADY_SUBSCRIBED: pwszFailureType = L"VSS_E_WRITER_ALREADY_SUBSCRIBED"; break;

case NOERROR:
default:
break;
}

return (pwszFailureType);
}


// This function displays the formatted message at the console and throws
// The passed return code will be returned by vsreq.exe
void Error(
IN INT nReturnCode,
IN const WCHAR* pwszMsgFormat,
IN ...
)
{
va_list marker;
va_start( marker, pwszMsgFormat );
vwprintf( pwszMsgFormat, marker );
va_end( marker );

//BS_ASSERT(FALSE);
// throw that return code.
throw(nReturnCode);
}

Ensuite debug.h

#define CHECK_SUCCESS( Call ) \
{ \
hr = Call; \
if (hr != S_OK) \
Error(1, L"\nError in %S(%d): \n\t- Call %S not succeeded. \n" \
L"\t Error code = 0x%08lx. Error description = %s\n", \
__FILE__, __LINE__, #Call, hr, GetStringFromFailureType(hr)); \
}

#define CHECK_NOFAIL( Call ) \
{ \
hr = Call; \
if (FAILED(hr)) \
Error(1, L"\nError in %S(%d): \n\t- Call %S not succeeded. \n" \
L"\t Error code = 0x%08lx. Error description = %s\n", \
__FILE__, __LINE__, #Call, hr, GetStringFromFailureType(hr)); \
}



void Error(INT nReturnCode, const WCHAR* pwszMsgFormat, ...);
LPCWSTR GetStringFromFailureType(HRESULT hrStatus);
LPCWSTR WszFromRestoreTarget(VSS_RESTORE_TARGET rt);
LPCWSTR WszFromFileRestoreStatus(VSS_FILE_RESTORE_STATUS rs);
void PrintPartialFiles(IVssComponent *pComponent);
void PrintDifferencedFiles(IVssComponent* pComponent);
void PrintNewTargets(IVssComponent *pComponent);
void PrintDirectedTargets(IVssComponent *pComponent);
void PrintRestoreSubcomponents(IVssComponent *pComponent);

Il faudra aussi: BasicFunctions.cpp

#include "stdafx.h"

//Copier un fichier, honteusement pompé sur la faq Developpez
int CopyAFile(char const * const source, char const * const destination)
{
FILE* fSrc = NULL;
FILE* fDest = NULL;
char buffer[512];
int NbLu;

if ((fSrc = fopen(source, "rb")) == NULL)
{
int err = errno;
perror("Cannot open file");
printf("Cannot open source file: %s, error=%d\n", source, err);
exit(1);
}

if ((fDest = fopen(destination, "wb")) == NULL)
{
int err = errno;
perror("Cannot open file");
printf("Cannot open destination file: %s, error=%d\n", destination, err);
fclose(fSrc);
exit(1);
}

while ((NbLu=fread(buffer, 1, 512, fSrc)) != 0)
fwrite(buffer, 1, NbLu, fDest);

fclose(fDest);
fclose(fSrc);

return 0;
}

//Convertir un WCHAR en CHAR, honteusement pompé sur la faq Developpez
void wtoc(CHAR* Dest, const WCHAR* Source)
{
int i =0;
while(Source[i] != '\0')
{
Dest[i] = (CHAR)Source[i];
i++;
}
}


Et voici le code principal

#include "stdafx.h"
#include "Vss.h"
#include "VsWriter.h"
#include "VsBackup.h"
#include "atlbase.h"
#include "atlconv.h"
#include "stdlib.h"
#include "stdio.h"
#include
#include
#include
#include
#include
//#include "BasicFunctions.h"
//#include "macros.h"
//#include "Error.h"
#pragma comment (lib, "VssApi.lib")

int _tmain(int argc, _TCHAR* argv[])
{
//Déclaration des fonctions
int CopyAFile(char const * const source, char const * const destination);
void wtoc(CHAR* Dest, const WCHAR* Source);
void BSTRtoASC (BSTR str, char * &strRet);
void ASCtoBSTR (char * str, BSTR * strRet);
int Split(char const * const FileToSplit);

//Initialisation des composants COM
CoInitializeEx(NULL, COINIT_MULTITHREADED);
CoInitializeSecurity
(
NULL,
-1,
NULL,
NULL,
RPC_C_AUTHN_LEVEL_CONNECT,
RPC_C_IMP_LEVEL_IMPERSONATE,
NULL,
EOAC_NONE,
NULL
);
HRESULT hr;
VSS_ID pSnapShotSetId;
VSS_ID pSnapShotId;
CComBSTR pbstrBackupXML = L"";
CComBSTR pbstrWriterXML = L"";
VSS_SNAPSHOT_PROP pSnapShotProperties;
VSS_PWSZ pSnapShotDevice;
BOOL IsVolumeSupported = TRUE;

//Initialisation du SnapShot
CComPtr pBackupComponent;
CHECK_SUCCESS(CreateVssBackupComponents(&pBackupComponent));
CHECK_SUCCESS(pBackupComponent->InitializeForBackup(pbstrBackupXML));
CHECK_SUCCESS(pBackupComponent->SetBackupState(TRUE, TRUE, VSS_BT_FULL, FALSE));
CComPtr pIVssAsync1;
CHECK_SUCCESS(pBackupComponent->GatherWriterMetadata(&pIVssAsync1));
CHECK_SUCCESS(pIVssAsync1->Wait());

//Pour la restauration: Combien de writers présents et lequel utiliser?
UINT cWriters = 0;
UINT iWriters = 0;
UINT TabInit = 0;
CHECK_SUCCESS(pBackupComponent->GetWriterMetadataCount(&cWriters));
printf("\nNombre de Writers presents: %i\n", cWriters);
VSS_ID idWriterInstance;
VSS_ID idWriter;
BSTR pWriterName;
VSS_USAGE_TYPE pUsage;
VSS_SOURCE_TYPE pSource;
CComPtr pExamineWriterMetadata;
for (iWriters = 0; iWriters <>
{
CHECK_SUCCESS(pBackupComponent->GetWriterMetadata(iWriters, &idWriterInstance, &pExamineWriterMetadata));
CHECK_SUCCESS(pExamineWriterMetadata->GetIdentity(&idWriterInstance, &idWriter, &pWriterName, &pUsage, &pSource));
if (((pUsage == 2) || (pUsage == 3)) && (pSource == 3))
{
TabInit ++;
}
}
//Maintenant que l'on sait lesquels, on recommence l'itération pour mettre les id dans un //tableau. ça ne marche pas terrible, ya un truc qui m'a échappé. Pas grave, de toute façon, il //faut que h'écrive mon writer...
printf("Nombre de Writers qui correspondent: %i\n", TabInit);
UINT SizeTab = (TabInit * 2);
VSS_ID TabWriter[sizeof(SizeTab)];
TabInit = 0;
for (iWriters = 0; iWriters <>
{
TabWriter[iWriters] = GUID_NULL;
}
for (iWriters = 0; iWriters <>
{
CHECK_SUCCESS(pBackupComponent->GetWriterMetadata(iWriters, &idWriterInstance, &pExamineWriterMetadata));
CHECK_SUCCESS(pExamineWriterMetadata->GetIdentity(&idWriterInstance, &idWriter, &pWriterName, &pUsage, &pSource));

if (((pUsage == 2) || (pUsage == 3)) && (pSource == 3))
{
printf("\nidWriterInstance: %li\n", idWriterInstance);
printf("idWriter: %li\n", idWriter);
printf("WriterName: %ls\n", pWriterName);
printf("Usage: %i\n", pUsage);
printf("Source: %i\n", pSource);
TabWriter[TabInit] = idWriterInstance;
TabInit ++;
TabWriter[TabInit] = idWriter;
TabInit ++;

}
}
//Pour la restauration\

CHECK_SUCCESS(pBackupComponent->StartSnapshotSet(&pSnapShotSetId));

//Vérification que le volume supporte les SnapShots...
CHECK_SUCCESS(pBackupComponent->IsVolumeSupported(GUID_NULL, L"C:\\", &IsVolumeSupported));
if (IsVolumeSupported == TRUE)
{
printf("\n***Le volume supporte les SnapShots, on continue...***\n\n");
}
else
{
printf("***Le volume ne supporte pas les SnapShot, on arrête...dommage***\n\n");
exit(1);
}

//Pour la restauration:
CHECK_SUCCESS(pBackupComponent->AddComponent(TabWriter[0], TabWriter[1], VSS_CT_FILEGROUP, NULL, L"MyBackup"));
//Pour la restauration\

//SnapShot du volume C:
CHECK_SUCCESS(pBackupComponent->AddToSnapshotSet(L"C:\\", GUID_NULL, &pSnapShotId));
CComPtr pIVssAsync2;
CHECK_SUCCESS(pBackupComponent->DoSnapshotSet(&pIVssAsync2));
CHECK_SUCCESS(pIVssAsync2->Wait());

//On récupère les info du Snapshot pour copier un ficher verrouillé
CHECK_SUCCESS(pBackupComponent->GetSnapshotProperties(pSnapShotId, &pSnapShotProperties));
pSnapShotDevice=pSnapShotProperties.m_pwszSnapshotDeviceObject;
printf("Lecteur virtuel du SnapShot: %ws\n\n", pSnapShotDevice);
WCHAR *FileToSave = L"\\Documents and Settings\\scolyo\\NTUSER.DAT";
printf("Fichier a sauvegarder: %ls\n\n", FileToSave);
wcsncat(pSnapShotDevice, FileToSave, 100);
printf("Chemin complet du fichier a sauvegarder: %ls\n\n", pSnapShotDevice);
WCHAR *DestFile = L"C:\\BETest\\NTUSER.DAT";

CHAR SourceFile[450+1] = "null";
wtoc(SourceFile, pSnapShotDevice);
printf("\nSourceFile: %s\n\n", SourceFile);
CHAR DestinationFile[450+1] = "null";
wtoc(DestinationFile, DestFile);
printf("\nDestinationFile: %s\n\n", DestinationFile);

CopyAFile(SourceFile, DestinationFile);

CHECK_SUCCESS(pBackupComponent->SaveAsXML(&pbstrBackupXML));
printf("String BackupXML: %ls\n\n", pbstrBackupXML);

//On créer le document XML du BackupComponent de la sauvegarde, nécessaire pour la restauration
//Convertissons un bstr en char
char * CharXML = NULL;
BSTRtoASC(pbstrBackupXML, CharXML);
FILE* FileXML = NULL;
FileXML = fopen("C:\\BETest\\BackupComponent.xml", "w");
if (FileXML != NULL)
{
fputs(CharXML, FileXML);
fclose(FileXML);
}
else
{
printf("Cannot create XML file: %s\n\n", pbstrBackupXML);
}

//Pour la restauration
CHECK_SUCCESS(pExamineWriterMetadata->SaveAsXML(&pbstrWriterXML));
printf("String WriterXML %ls\n\n", pbstrWriterXML);
CharXML = NULL;
BSTRtoASC(pbstrWriterXML, CharXML);
FileXML = NULL;
FileXML = fopen("C:\\BETest\\WriterComponent.xml", "w");
if (FileXML != NULL)
{
fputs(CharXML, FileXML);
fclose(FileXML);
}
else
{
printf("Cannot create XML file: %s\n\n", pbstrWriterXML);
}
//Pour la restauration\

//On détruit proprement le snapshot avant de quitter
LONG pDeletedSnapShot;
VSS_ID pNonDeletedSnapShotId;
CHECK_SUCCESS(pBackupComponent->DeleteSnapshots(pSnapShotId, VSS_OBJECT_SNAPSHOT, TRUE, &pDeletedSnapShot, &pNonDeletedSnapShotId));
printf("***SnapShot wiped ***\n");

return 0;
}

Voila, avec ce code, vous êtes capable de copier un fichier verrouiller (modifier les lignes en rouge pour les adapter à votre cas).

Pour plus de détails, consulter les pages du msdn:

http://msdn.microsoft.com/en-us/library/aa384649(VS.85).aspx

Vous pouvez aussi me contacter, si je peux aider, ce sera avec plaisir ;-)