Category Archives: MIM/FIM

Synchronization Service – supported runtime

2015-04-08

Ostatnio uruchamiając synchronizację na jednym z agentów otrzymałem następujący błąd dotyczący własnego rozszerzenia:

The management agent failed on run profile. The run step stopped because a required rules extension „Demo.FIM.ActiveDirectory.dll” could not be loaded.

Po upewnieniu się, że wspomniana biblioteka znajduje się w katalogu Extensions zajrzałem do loga systemowego. Tam opis błędu był znacznie bardziej rozbudowany i wyglądał tak:

Verify that the rules extension is located in the Extensions directory. If the extension is present, confirm that the version of the .NET framework  that can run the extension is installed on the server and that a supportedRuntimes entry in the configuration files specifies that version. The synchronization engine will not be able to load an extension that is built with a newer version of the .NET framework than the version of the .NET runtime it is hosting.

Oraz tak:

Could not load file or assembly ‚file:///C:\Program Files\Microsoft Forefront Identity Manager\2010\Synchronization Service\Extensions\Demo.FIM.ActiveDirectory.dll’ or one of its dependencies. Operation is not supported. (Exception from HRESULT: 0x80131515)

An attempt was made to load an assembly from a network location which would have caused the assembly to be sandboxed in previous versions of the .NET Framework. This release of the .NET Framework does not enable CAS policy by default, so this load may be dangerous. If this load is not intended to sandbox the assembly, please enable the loadFromRemoteSources switch. See http://go.microsoft.com/fwlink/?LinkId=155569 for more information.

Oczywiście wszystkie wymagane wersje frameworków były zainstalowane. Po krótkich poszukiwaniach w internecie okazało się, że błąd ten jest ogólnie znany i można go rozwiązać w następujący sposób:

      1. Otwieramy plik konfiguracyjny procesu Synchronization Service (miiserver.exe): C:\Program Files\Microsoft Forefront Identity Manager\2010\Synchronization Service\Bin\miiserver.exe.config
      2. Odnajdujemy element startup:
        <startup useLegacyV2RuntimeActivationPolicy="true">
          <supportedRuntime version="v4.0"></supportedRuntime>
          <supportedRuntime version="v2.0.50727"></supportedRuntime>
        </startup>
        
      3. Zamieniamy kolejność wersji supportedRuntime:
        <startup useLegacyV2RuntimeActivationPolicy="true">
          <supportedRuntime version="v2.0.50727"></supportedRuntime>
          <supportedRuntime version="v4.0"></supportedRuntime>
        </startup>
        

Powyższa operacja rozwiązała problem z tym konkretnym agentem. Dodam tylko, że jego rozszerzenie było skompilowane w wersji .NET 3.5 i korzystało również z bibliotek w wersji .NET 2.0. Okazało się jednak, że w tym momencie taki sam błąd zaczął pojawiać się przy uruchamianiu innego agenta (typu ECMA), którego kod z powodu wykorzystywanych zależności był skompilowany w wersji .NET 4.5. Jak więc poradzić sobie w tej sytuacji skoro dwóch agentów wymaga dwóch różnych konfiguracji? Można jednego z nich uruchamiać w kontekście Synchronization Service, a drugiego w oddzielnym procesie z inną konfiguracją. Wystarczy dla drugiego agenta zaznaczyć opcję „Run this management agent in a separate process”, a następnie otworzyć plik konfiguracyjny dla agentów uruchamianych w oddzielnym procesie „C:\Program Files\Microsoft Forefront Identity Manager\2010\Synchronization Service\Bin\mmsscrpt.exe.config” i w nim ustawić supportedRuntime w następujący sposób:

<startup useLegacyV2RuntimeActivationPolicy="true">
  <supportedRuntime version="v4.0"></supportedRuntime>
  <supportedRuntime version="v2.0.50727"></supportedRuntime>
</startup>
Reklamy

Zmiana Capabilities dla istniejącego agenta

2014-10-03

Podczas budowy agenta typu Extensible Connection 2.0 w naszej klasie implementujemy między innymi interfejs IMAExtensible2GetCapabilities. Wymaga to dodania do niej publicznej właściwości Capabilities zwracającej obiekt typu MACapabilities. Obiekt ten posiada szereg ustawień konfigurujących pracę naszego agenta. Oto przykładowa implementacja tej właściwości:

public MACapabilities Capabilities
{
  get
  {
    return new MACapabilities()
      {
        ConcurrentOperation = false,
        DeltaImport = false,
        DistinguishedNameStyle = MADistinguishedNameStyle.Generic,
        DeleteAddAsReplace = false,
        ExportType = MAExportType.AttributeReplace,
        NoReferenceValuesInFirstExport = false,
        SupportExport = true
      };
  }
}

Podczas dodawania agenta do Synchronization Service jednym z kroków jest Capabilities:

MaCapabilities

Właśnie w tym momencie następuje odwołanie do właściwości Capabilities w klasie naszego agenta i odczyt znajdujących się tam ustawień. Niestety tu pojawia się pewien problem. Odczyt ten następuje tylko podczas dodawania nowego agenta i pobrane ustawienia są dla niego zapamiętywane w aktualnej postaci. Jeżeli później wejdziemy w edycję istniejącego agenta to kroku Capabilities już nie zobaczymy. Co więcej, nawet jak wyeksportujemy agenta do pliku i następnie utworzymy nowego poprzez import, to ustawienia Capabilities nie zostaną wczytane z biblioteki a jedynie odtworzone z pliku eksportu. Co to oznacza? Jeżeli zmodyfikujemy ustawienia MACapabilities w naszej klasie to pomimo podmiany biblioteki agent cały czas będzie działał zgodnie z ustawieniami zapamiętanymi podczas jego tworzenia. Nie ma możliwości odświeżenia Capabilities dla istniejącego agenta więc nie jesteśmy w stanie zmienić dla niego tych ustawień (Refresh interfaces nie zadziała). Możemy oczywiście usunąć aktualnego agenta i utworzyć nowego, ale takie rozwiązanie przy rozbudowanym agencie (posiadającym kilka typów obiektów, do tego filtry oraz kilkanaście przepływów) oznacza żmudną i podatną na błędy pracę. Jaki jest więc sposób na wprowadzenie zmian w Capabilities dla istniejącego agenta? Ja stosuję następującą metodę:

  1. Modyfikujemy właściwość Capabilities w klasie agenta, kompilujemy i podmieniamy bibliotekę
  2. Dodajemy do Synchronization Service nowego agenta korzystającego z naszej biblioteki (pod dowolną nazwą, bez konfigurowania)
  3. Eksportujemy oryginalnego i nowego agenta do plików xml
  4. Usuwamy obydwu agentów z Synchronization Service
  5. Podmieniamy wartości elementów <capabilities-mask> i <capability-bits> w pliku xml oryginalnego agenta na wartości z pliku nowego agenta
  6. Importujemy oryginalnego agenta

W elementach <capabilities-mask> i <capability-bits> znajdują się ustawienia Capabilities zapisane podczas tworzenia agenta. Po zmianie ich na nowe wartości i imporcie nasz dotychczasowy agent będzie działał zgodnie z oczekiwaniami.

Użycie własnego atrybutu w Function Evaluator

2014-08-30

Jeżeli w FIM utworzymy nowy typ obiektu z własnymi atrybutami okazuje się, że atrybuty te nie są widoczne w konfiguracji activity Function Evaluator:

FunctionEvaluator1

FunctionEvaluator2

Na szczęście mimo iż nie można ich wybrać bezpośrednio z kontrolek, istnieje sposób odwołania się do nich. Wystarczy użyć wyrażenia [//Target/CustomAttribute] (oczywiście podając właściwą nazwę atrybutu). W przypadku atrybutu docelowego wystarczy wpisać powyższe wyrażenie do destination, z kolei aby odwołać się do wartości takiego atrybutu musimy użyć CustomExpression:

FunctionEvaluator3

Opisany problem nie dotyczy własnych atrybutów bindowanych do standardowych typów obiektów. W takim przypadku są one widoczne w kontrolkach razem z innymi atrybutami.

FIM Portal – dopuszczalne wartości atrybutu i Constant Specifier

2014-06-05

W dzisiejszym wpisie przedstawię pewien problem związany z walidacją wartości atrybutów w portalu FIM. Załóżmy, że dany obiekt posiada atrybut tekstowy, w którym mogą znaleźć się jedynie wartości z określonego zbioru. Chcemy aby użytkownik podczas tworzenia obiektu miał do wyboru dwie wartości atrybutu: MyValue-1 oraz MyValue-2. Standardowo zaczynamy od ustawienia walidacji bindingu na odpowiednie wyrażenie regularne:

fim1

Następnie w widoku tworzenia obiektu definiujemy kontrolkę dla naszego atrybutu. Będzie to DropDownList z ItemSource ustawionym na właściwość LocalizedAllowedValues atrybutu:

<my:Control my:Name="MailboxType" my:TypeName="UocDropDownList"
	my:Caption="{Binding Source=schema, Path=MailboxType.DisplayName}"
	my:Description="{Binding Source=schema, Path=MailboxType.Description}"
	my:RightsLevel="{Binding Source=rights, Path=MailboxType}">
	<my:Properties>
		<my:Property my:Name="Required" my:Value="{Binding Source=schema, Path=MailboxType.Required}" />
		<my:Property my:Name="ValuePath" my:Value="Value"/>
		<my:Property my:Name="CaptionPath" my:Value="Caption"/>
		<my:Property my:Name="HintPath" my:Value="Hint"/>
		<my:Property my:Name="ItemSource" my:Value="{Binding Source=schema, Path=MailboxType.LocalizedAllowedValues}"/>
		<my:Property my:Name="SelectedValue" my:Value="{Binding Source=object, Path=MailboxType, Mode=TwoWay}"/>
	</my:Properties>
</my:Control>

Teoretycznie wszystko zrobiliśmy poprawnie ale podczas tworzenia nowego obiektu okazuje się, że kontrolka DropDownList jest pusta. Oczekiwaliśmy, że będzie ona zwierała listę dopuszczalnych wartości dla interesującego nas atrybutu:

fim2

Przyczyną takiego zachowania jest użycie w wyrażeniu regularnym znaku „-„. Przy definiowaniu walidacji nie możemy używać znaków specjalnych takich jak  „-„, „.”, „?”, „$”, „|”, itp. No dobrze, ale co zrobić, jeżeli mimo wszystko chcemy aby w kontrolce wyświetlane były wartości z myślnikami? Rozwiązaniem jest użycie obiektów Constant Specifier. Wchodzimy do All Resources, wybieramy typ Constant Specifier i tworzymy nowe obiekty dla każdej z dopuszczalnych wartości. Podczas tworzenia obiektu Constant Specifier podajemy wartość jaka będzie wyświetlana w kontrolce, wartość klucza bez znaków specjalnych oraz wskazujemy atrybut i typ obiektu:

fim3

Teraz pozostało jedynie zmodyfikowanie wyrażenia regularnego w walidacji tak, aby zawierało wartości kluczy zamiast wartości wyświetlanych:

fim4

Po tej zmianie (i wykonaniu IISRESET) wszystko działa tak jak powinno, podczas tworzenia obiektu możemy wybrać jedną z dopuszczalnych wartości:

fim5

Powyżej pokazałem tylko jedno z zastosowań obiektów Constant Specifier. Generalnie pozwalają one na rozdzielenie dopuszczalnych wartości atrybutu na wartość fizycznie zapisywaną w obiekcie (Constant Value Key) oraz wartość wyświetlaną w UI (Display Name). Takie podejście jest dobrą praktyką z kilku powodów. Po pierwsze wartości atrybutów przechowywane w bazie danych mogą być niezrozumiałe dla użytkownika. Po drugie operowanie w kodzie na wartościach wyświetlanych powoduje konieczność jego modyfikacji przy każdej zmianie tych wartości. Ostatnią zaletą jest możliwość zdefiniowania wyświetlanych wartości dla różnych języków. Wystarczy podczas tworzenia obiektu Constant Specifier przejść na zakładkę Localization i dla poszczególnych języków (Supported Languages) podać wartość, jaka będzie wyświetlana w kontrolkach (Localized Display name).

FIM – Extensions Directory

2014-04-03

Podczas tworzenia rozszerzeń dla Synchronization Service często istnieje potrzeba wykorzystania własnych plików konfiguracyjnych. Zazwyczaj pliki takie umieszczamy razem z biblioteką w katalogu Extensions. Aby później odczytać plik konfiguracyjny musimy odwołać się do katalogu Extensions, i tu z pomocą przychodzi właściwość ExtensionsDirectory w klasie Microsoft.MetadirectoryServices.Utils. Właściwość ta zawiera pełną ścieżkę do interesującego nas katalogu. Oto przykład użycia własnego pliku konfiguracyjnego znajdującego się w katalogu Extensions:

public static KeyValueConfigurationCollection ReadConfigFromFile()
{
	string configPath = Path.Combine(Utils.ExtensionsDirectory, Constants.ConfigFile);
	if (!File.Exists(configPath))
	{
		throw new ConfigurationErrorsException(String.Format("{0} file not found.", configPath));
	}

	ExeConfigurationFileMap configMap = new ExeConfigurationFileMap();
	configMap.ExeConfigFilename = configPath;
	Configuration config = ConfigurationManager.OpenMappedExeConfiguration(configMap, ConfigurationUserLevel.None);
	AppSettingsSection section = config.AppSettings;
	if (section == null)
	{
		throw new ConfigurationErrorsException(String.Format("Invalid or missing appSettings section in {0} file.", Constants.ConfigFile));
	}

	return section.Settings;
}

Więcej informacji o klasie Microsoft.MetadirectoryServices.Utils: MSDN