ラベル Azure AD Connect の投稿を表示しています。 すべての投稿を表示
ラベル Azure AD Connect の投稿を表示しています。 すべての投稿を表示

2020年5月2日土曜日

Azure ADの外部コラボレーションとBYOID

こんにちは、富士榮です。

リモートワークで組織外の方々とのコラボレーションが大きなテーマになっている方々も多いと思いますが、そういう時はAzure ADの外部連携(B2B)の機能ですよね。

そう言えばAzure ADのDirect FederationがGoogle以外にもSAML/ws-federationの外部IdPをサポートした、という会話をfacebookで某安納さんがしていたのを思い出したのと、先日 Alex Simons が色々と新しい機能が出たよ、というもはや個別の機能リリースの紹介だと無理だから一気にまとめて発表、みたいなポストをしていた中に、Azure AD B2CのSAMLサポートが正式リリースされた、という話があったので、もしやこれは繋がる???ということでやってみました。
いわゆるBYOD(Bring Your Own Identity)ってやつです。

当然、Azure ADのカスタムドメインを使った外部ID連携でも良いわけですが、この辺は後で比較をしてきます。


まず関連する記事、ドキュメント類を。


とりあえず完成イメージを

言葉で説明しても伝わりにくいと思うので、どういうことが出来るようになるのか?を動画にしています。
Azure AD B2Bで招待した外部ユーザでログインしようとすると、Azure AD B2Cにリダイレクトされ、LINEでログインすると、Azure ADにゲストユーザとして登録されてアプリケーションが使えるようになる、というシナリオです。


Azure AD B2CのSAMLサポート

先に書いた公式ドキュメント通りに設定していくと、Azure AD B2CがSAML IdPとして動作出来るようになります。(カスタムポリシーを使います。慣れると非常に簡単です)

大きな流れは以下の通りです。

Azure AD B2C側の設定
  • アサーション署名用の証明書(PFX)を作ってポリシーキーとしてアップロードする
  • ClaimsProviderとして、SAML2AssertionIssuerを作成する
    • MetadataにIssuerUriとして設定したものがIdPのEntityIDになります
    • CryptographicKeysに先にアップロードした証明書コンテナを指定するとアサーション署名をしてくれます(暗号化用証明書もアップロードすれば暗号化もできます)
こんな感じの設定になります。
<ClaimsProvider>
  <DisplayName>Token Issuer</DisplayName>
  <TechnicalProfiles>

    <!-- SAML Token Issuer technical profile -->
    <TechnicalProfile Id="Saml2AssertionIssuer_SAMPLE">
      <DisplayName>Token Issuer</DisplayName>
      <Protocol Name="SAML2"/>
      <OutputTokenFormat>SAML2</OutputTokenFormat>
      <Metadata>
        <Item Key="IssuerUri">https://nfpoc.b2clogin.com/nfpoc.onmicrosoft.com/B2C_1A_SI_SAML_SAMPLE</Item>
      </Metadata>
      <CryptographicKeys>
        <Key Id="MetadataSigning" StorageReferenceId="B2C_1A_samlsampleapp"/>
        <Key Id="SamlAssertionSigning" StorageReferenceId="B2C_1A_samlsampleapp"/>
        <Key Id="SamlMessageSigning" StorageReferenceId="B2C_1A_samlsampleapp"/>
      </CryptographicKeys>
      <InputClaims/>
      <OutputClaims/>
      <UseTechnicalProfileForSessionManagement ReferenceId="SM-Saml-issuer"/>
    </TechnicalProfile>

    <!-- Session management technical profile for SAML based tokens -->
    <TechnicalProfile Id="SM-Saml-issuer">
      <DisplayName>Session Management Provider</DisplayName>
      <Protocol Name="Proprietary" Handler="Web.TPEngine.SSO.SamlSSOSessionProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
    </TechnicalProfile>
  </TechnicalProfiles>
</ClaimsProvider>


  • UserJourneyを定義する
    • この辺は通常のカスタムポリシーと同じです
  • RelyingPartyの定義を行う
    • 公式ドキュメントだとSAML SPに関するパラメータはAzure AD B2Cにアプリケーション定義を作ってマニフェストで設定する、ということになっていますが、Azure ADとB2Bとして連携する場合は、ちょっと工夫が必要です(後述します)

Azure AD側の設定

Azure ADの直接フェデレーションの設定を行います。
  • プロトコルはSAMLを選ぶ
  • IdPのドメイン名はSAML IdPの認証エンドポイントと同一ドメインである必要があるため、Azure AD B2Cのドメイン名(~.b2clogin.com)を指定する
  • 上記でAzure AD B2Cの設定が上手くいっていればSAML Metadataのダウンロードが出来るようになっていますので、保存したMetadataをアップロードして解析を行う
直接フェデレーションの設定画面。ここで「新しいSAML/WS-Fed IdP」を選びます。

こんな感じで設定します。



設定としては非常にシンプルです。
ただ、何点かクセがありますので、その部分を重点的に書いておきます。

設定のポイント

Azure AD B2Cのアプリケーション設定
  • ドキュメントを見ると、Azure AD B2Bと直接連携するためには以下の情報を設定する必要があることがわかります。
    • AssertionConsumerService
      • https://login.microsoftonline.com/login.srf
    • Audience(日本語ドキュメントだと「対象ユーザー」となっていますが。。。SAML SPのEntityIDのことです)
      • urn:federation:MicrosoftOnline
  • しかし、現状のAzure AD B2Cのアプリケーション登録ではカスタムスキームのAudience(urn:federation:MicrosoftOnline)をマニフェスト編集をしても登録することが出来ません。
  • ということで、SP(Azure AD)のMetadataを作って適当なところ(今回はbob)にアップロードし、カスタムポリシーから参照する形をとります。
こんな感じです。
<RelyingParty>
  <DefaultUserJourney ReferenceId="SI_SAML_SAMPLE" />
    <TechnicalProfile Id="PolicyProfile">
    <DisplayName>PolicyProfile</DisplayName>
    <Protocol Name="SAML2"/>
    <Metadata>
       <Item Key="PartnerEntity">https://nfpoccontent.blob.core.windows.net/root/aad_sp_meta.xml</Item>

手動で作ったSP Metadataはこんな感じです。単純にEntityIDとエンドポイントさえ書いてあれば最低限はOKです。

<?xml version="1.0"?>
<md:EntityDescriptor
    xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"
    xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
    entityID="urn:federation:MicrosoftOnline"
    validUntil="2031-12-31T00:00:00.000Z">
  <md:SPSSODescriptor
    protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
    <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://login.microsoftonline.com/login.srf"/>
    <md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://login.microsoftonline.com/login.srf" index="0"/>
  </md:SPSSODescriptor>
</md:EntityDescriptor>

Azure AD B2CのSAML Metadata

  • 一応ドキュメントを漁ると出てくるのですが、見落としがちなので書いておきますが、以下のルールでMetadata URLが生成されます。
    • https://ドテナント名.b2clogin.com/テナント名.onmicrosoft.com/B2C_1A_ポリシー名/Samlp/metadata
Azure AD B2CからAzure ADへ渡す属性(SAML AssertionのAttributeStatement)

  • ドキュメントを見るとnameidがpersistentであること、emailaddressを属性として渡すこと、とありますので、それに合わせてAzure AD B2CのRelyingParty設定を行います。具体的にはOutputClaimsの設定です。
  • また、標準的なAzure AD B2CのSAML設定だと結構冗長な感じでAttributeStatementが書かれるので、TechnicalProfileのMetadataにSaml11AttributeEncodingInfo(Saml20~でもOK)を指定することで少しすっきりします。
こんな感じのRelyingParty定義になります。

<RelyingParty>
  <DefaultUserJourney ReferenceId="SI_SAML_SAMPLE" />
  <TechnicalProfile Id="PolicyProfile">
    <DisplayName>PolicyProfile</DisplayName>
      <Protocol Name="SAML2"/>
      <Metadata>
        <Item Key="PartnerEntity">https://nfpoccontent.blob.core.windows.net/root/aad_sp_meta.xml</Item>
        <Item Key="Saml11AttributeEncodingInfo">
          <![CDATA[
            <saml:AttributeStatement xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion">
            <saml:Attribute AttributeName="emailaddress" AttributeNamespace="http://schemas.xmlsoap.org/ws/2005/05/identity/claims">
            <saml:AttributeValue>
            </saml:AttributeValue>
            </saml:AttributeStatement>]]></Item>
      </Metadata>
      <OutputClaims>
        <OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="objectId"/>
        <OutputClaim ClaimTypeReferenceId="b2cmail" PartnerClaimType="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress" />
      </OutputClaims>
      <SubjectNamingInfo ClaimType="objectId" ExcludeAsClaim="true"/>
    </TechnicalProfile>
  </RelyingParty>
</TrustFrameworkPolicy>

ちなみにClaimType「b2cmail」は適当に適宜した属性なので自由に定義してもらえれば良いかと思います。重要なのは、「Azure AD B2Cのドメインと同じドメイン名を持つメールアドレスが設定されていること」です。例えば、hoge.b2clogin.comというAzure AD B2Cドメインを使っている場合は、fuga@hoge.b2clogin.comという値を返す必要があります。
※この辺りは早くAzure AD B2Cがカスタムドメインをサポートしてくれないと実利用するには辛いですね。

いざ、動作確認

設定はこれでおしまいです。
しかし、あくまでこれはAzure AD B2Bにおける「外部ユーザの招待において独自のパスワードを払い出さずに済ませるための機能(これ重要)」なので、あらかじめユーザを招待しておく必要があります。

ここは通常の招待と同じなので詳細は割愛しますが、Azure AD B2Cのドメインと同じドメインのユーザを指定して招待する、というところがポイントです。
つまり、メールは届きません。(少なくともb2clogin.comを使っている限り、一般人はこのドメインでメールは届かないと思います)

ただ、招待されている状態であれば招待元のアプリケーションにアクセスすれば招待の承認と同じことが起きますので、運用でカバーです。



そして、これも公式ドキュメントに記載されていますが、現状B2Bの直接フェデレーションはマルチテナントアプリケーションでのホームレルムディスカバリが使えないので、招待元ディレクトリのテナントIDやドメイン名が明示的に指定されているアプリケーションしか動きません。
例えば、

  • https://myapps.microsoft.comはダメ
  • https://myapps.microsoft.com/?tenantid=xxxxxxxxはOK
という感じです。


ここまで行けば、冒頭に動画で紹介した動きが再現できるはずです。
ちなみに動画内では条件付きアクセスを使ってゲストユーザの初回ログイン時に利用規約に同意させる様にしています。

カスタムドメイン単位でのフェデレーションと何が違うのか?

ここで疑問が出てくるのが、元々Azure ADにはカスタムドメイン単位で外部のSAML/WS-FederationのIdPと連携する機能があります。ちょっと前に多くの企業がAD FSをオンプレにおいてOffice365とSSOをやっていた構成ですね。

もちろんこのケースは社内ユーザを想定した話ですが、技術的に言うとB2Bの直接フェデレーションとほぼ変わりません。

ただ、細かく見るとちょっとずつ違います。
非常に雑な比較ですが、こんな感じです。

カスタムドメインのID連携B2Bの直接フェデレーション
フェデレーション単位ドメイン単位ドメイン単位
ユーザの管理あらかじめ作成が必要(Azure AD Connectでの同期など)
ImmutableIdでのマッチング
あらかじめ招待が必要
メールアドレスでのマッチング
アプリケーションの制限マルチテナントでも可
また、WS-Fedでwindowstranportエンドポイントの整備などの条件を満たせばWindows 10 PCログオンも可(Webサインイン設定不要)
マルチテナントアプリは不可
当然Windows 10 PCログインには使えない




ということで、実用的かどうかはユースケース次第というところですが、日々色々な機能が拡張されてきているな、というところです。



2018年5月18日金曜日

[Azure AD] Azure AD ConnectでPingFederateとのSSO構成が可能に!

こんにちは、富士榮です。

ちょうど1年近く前に正式リリースされたAzure AD Web Application ProxyとPingIdentityのPingAccess連携の話に引き続き、今度はAzure AD Connectを経由したPingFederateの構成が発表されました。(現状Public Previewです)

 [Azure AD] PingAccess連携が正式リリース
 https://idmlab.eidentity.jp/2017/06/azure-ad-pingaccess.html

対応したのは最新バージョンのAzure AD Connect(1.1.819.0)からです。

Office365、Azure ADとのID連携先としてPingFederateは早い段階から認定されていましたが、より深くインテグレーションされてきました。

関連ドキュメントはこの辺りから。



簡単に言うと、自動的にやってくれることは、

  • PingFederate側に何を設定すれば良いかを教えてくれる(Azure ADのEntityIDとかエンドポイントのURIとか)
  • Azure AD側のFederationの構成してくれる

ということです。

今、手元にPingFederateの環境がないので、インストーラの途中までですが、こんな感じで設定を行います。

User sign-inの設定で「Federation with PingFederate」が選択できるようになっています。

連携するAzure ADのドメインを選択します。

Federationの設定を行うところでは、PingFederateに何を設定するかExportしてくれるのと、PingFederateのURLを指定します。

ちなみにExportした構成情報は結構丁寧にPingFederateに何を設定したらいいのか記載されています。


まぁ正直、手動で構成するのもそれほど手間ではないのですが、最初から選択肢として組み込まれてくるとグッときますね。
皆さんも自宅に転がっているPingFederateを使って是非試してみてください。

2017年8月14日月曜日

APIゲートウェイでSaaSのIDを簡単に管理する②

こんにちは、富士榮です。

前回に引き続き、CDATA API ServerとID管理システムを使ってSaaS(今回はSalesforce.com)へのプロビジョニングを簡単に行う、という仕組みを作っていきます。

前回は、API ServerをSalesforce.comのUserテーブルへ接続し、ユーザの取得をしましたが、今回はいよいよID管理システム(今回はAzure AD Connect)を使ってユーザを作成します。

やることは非常に単純で、API Serverで定義されたリソース(実際はSalesforce.comのUserテーブル)に対してJSON形式で作成したいユーザの情報をPOSTするだけです。

◆投げ込むJSONを確認する

Salesforce.comの開発者ドキュメントを見ると、Userテーブルに新規レコード(つまり新規ユーザ)を作成する際に必須となる属性を確認します。

 SOAP API開発者ガイド)User
 https://developer.salesforce.com/docs/atlas.ja-jp.api.meta/api/sforce_api_objects_user.htm

これを見ると、以下の属性が必須との事です。
  • Username :ユーザ識別子。メールアドレス形式
  • Alias :ユーザの別名
  • Email :ユーザのメールアドレス
  • LastName :ユーザの姓
  • EmailEncodingKey :メールの文字コード
  • LanguageLocaleKey :ユーザの言語
  • LocaleSidKey :ユーザのロケール
  • ProfileId :ユーザのプロファイルのID
  • TimeZoneSidKey :ユーザのタイムゾーン
  • UserPermissionsOfflineUser :オフラインエディションの使用可否
  • UserPermissionsMarketingUser :キャンペーン管理の可否

ユーザを作成するには、最低限これらの値をJSON形式でAPI ServerのリソースへPOSTすればよい、ということなので、まずは直接POSTしてみます。

ただ、EmailEncodingKeyやProfileIdなど、何を値として設定すれば良いのかよくわからない属性もあるので、まずはSalesforce.com上に手動で作成したユーザをGETし、各属性にどのような値が入っているのかを確認すると以下のような値が入っていることがわかるので、この例をテンプレートにPOSTするデータを作成します。

属性
Usernamexxxxx@example.com
AliasFujie
Emailxxxxx@example.com
LastNameFujie
EmailEncodingKeyISO-2022-JP
LanguageLocaleKeyja
LocaleSidKeyja_JP
ProfileId(Chatter Free Userの場合)00e7F000001LotXQAS
TimeZoneSidKeyAsia/Tokyo
UserPermissionsOfflineUserfalse
UserPermissionsMarketingUserfalse


以下のようなJSONを作成し、Chrome ExtensionのAdvanced REST Clientを使ってAPI Serverに定義したリソースエンドポイントへPOSTをします。
{
  "Username": "test999@eidentity.jp",
  "Alias": "test999",
  "Email": "test999@eidentity.jp",
  "LastName": "Test",
  "EmailEncodingKey": "ISO-2022-JP",
  "LanguageLocaleKey": "ja",
  "LocaleSidKey": "ja_JP",
  "ProfileId": "00e7F000001LotXQAS",
  "TimeZoneSidKey": "Asia/Tokyo",
  "UserPermissionsOfflineUser": "false",
  "UserPermissionsMarketingUser": "false"
}


上手くいくと、HTTP 201 Createdが返ってきて実際にユーザが作成されます。


Salesforce.comの管理画面からユーザを確認すると実際に作成されていることがわかります。


◆管理エージェントの作成

投げ込むJSONがわかったところで、あとはID管理システムでJSONの生成からPOSTを自動化するだけです。

やることはFIM/MIM向けのECMA2エージェントとして以前公開したGeneric REST API Management Agentとよく似たようなことなので、このコードをカスタマイズしてみます。

細かいECMA2エージェントの開発方法は別エントリでそのうち解説しますが、以下の手順でGeneric REST API Management Agentを改造します。

1.スキーマ定義(GetSchemaメソッド)
 先にリストアップした必須属性を定義します。Username属性をAnchor属性として設定をしています。こんなコードになります。
                SchemaType personType = SchemaType.Create("Person", false);
                personType.Attributes.Add(SchemaAttribute.CreateAnchorAttribute("Username", AttributeType.String));
                personType.Attributes.Add(SchemaAttribute.CreateSingleValuedAttribute("Alias", AttributeType.String));
                personType.Attributes.Add(SchemaAttribute.CreateSingleValuedAttribute("Email", AttributeType.String));
                personType.Attributes.Add(SchemaAttribute.CreateSingleValuedAttribute("LastName", AttributeType.String));
                personType.Attributes.Add(SchemaAttribute.CreateSingleValuedAttribute("EmailEncodingKey", AttributeType.String));
                personType.Attributes.Add(SchemaAttribute.CreateSingleValuedAttribute("LanguageLocaleKey", AttributeType.String));
                personType.Attributes.Add(SchemaAttribute.CreateSingleValuedAttribute("LocaleSidKey", AttributeType.String));
                personType.Attributes.Add(SchemaAttribute.CreateSingleValuedAttribute("ProfileId", AttributeType.String));
                personType.Attributes.Add(SchemaAttribute.CreateSingleValuedAttribute("TimeZoneSidKey", AttributeType.String));
                personType.Attributes.Add(SchemaAttribute.CreateSingleValuedAttribute("UserPermissionsOfflineUser", AttributeType.Boolean));
                personType.Attributes.Add(SchemaAttribute.CreateSingleValuedAttribute("UserPermissionsMarketingUser", AttributeType.Boolean));
                Schema schema = Schema.Create();
                schema.Types.Add(personType);
                return schema;

 こうすることで管理エージェントで管理対象属性を選択できるようになります。


2.パラメータ定義(GetConfigParametersメソッド)
 API Server上のリソースURIや接続に使うAPI ServerユーザのAuthTokenを指定できるようにします。こんなコードになります。接続パラメータの部分だけで良いのでConnectivityの部分にパラメータを追加しています。
                switch (_page)
                {
                    case ConfigParameterPage.Capabilities:
                        break;
                    case ConfigParameterPage.Connectivity:
                        _configParametersDefinitions.Add(ConfigParameterDefinition.CreateLabelParameter("Connection parameters"));
                        _configParametersDefinitions.Add(ConfigParameterDefinition.CreateStringParameter(ConstDefinition.CFG_RESOURCE_URI, ""));
                        _configParametersDefinitions.Add(ConfigParameterDefinition.CreateStringParameter(ConstDefinition.CFG_AUTH_TOKEN, ""));
                        break;
                    case ConfigParameterPage.Global:
                        break;
                    case ConfigParameterPage.Partition:
                        break;
                    case ConfigParameterPage.RunStep:
                        break;
                    case ConfigParameterPage.Schema:
                        break;
                }

尚、テストなので、ValidateConfigParametersについては処理を入れておらず、指定されたパラメータの値の確認などは行っていません。

これで、管理エージェント設定画面で接続関連のパラメータを指定することが出来るようになります。ここにAPI ServerのリソースURIとAuthTokenを指定します。

3.インポート処理関連(OpenImportConnection/GetImportEntriesメソッド)
 まずはOpenImportConnectionで、設定パラメータからリソースURIとAuthTokenの値を取り出し、後続のGetImportEntriesで使えるようにグローバル変数にセットしておきます。
                resource_uri = _configParameters[ConstDefinition.CFG_RESOURCE_URI].Value.ToString();
                auth_token = _configParameters[ConstDefinition.CFG_AUTH_TOKEN].Value.ToString();

 次に、API Server(を経由してSalesforce.com)からユーザ情報を取得するGetImportEntriesです。やることはシンプルで、API ServerのリソースURIへAuthToken付きのGETリクエストを投げ、取得したJSONをパースしてConnector Space上のエントリに追加していくだけです。尚、本来はページング処理などをする必要があるのですが、今回はテストなので省略しています。
                var _csentries = new List<CSEntryChange>();
                string _importEntriesJSON = utils.GetContentsWithAccessToken(resource_uri, auth_token, null);
                var _getImportEntriesByObjectTypeResult = JObject.Parse(_importEntriesJSON).SelectToken("value").ToString();
                var _importObjectJSONArray = JArray.Parse(_getImportEntriesByObjectTypeResult);
                foreach (var _importObjectJSON in _importObjectJSONArray)
                {
                    var _csentryChange = CSEntryChange.Create();
                    _csentryChange.ObjectModificationType = ObjectModificationType.Add;
                    _csentryChange.ObjectType = "Person";
                    _csentryChange.AttributeChanges.Add(AttributeChange.CreateAttributeAdd("Alias", _importObjectJSON["Alias"].ToString()));
                    _csentryChange.AttributeChanges.Add(AttributeChange.CreateAttributeAdd("Email", _importObjectJSON["Email"].ToString()));
                    _csentryChange.AttributeChanges.Add(AttributeChange.CreateAttributeAdd("Username", _importObjectJSON["Username"].ToString()));
                    _csentryChange.AttributeChanges.Add(AttributeChange.CreateAttributeAdd("LastName", _importObjectJSON["LastName"].ToString()));
                    _csentryChange.AttributeChanges.Add(AttributeChange.CreateAttributeAdd("EmailEncodingKey", _importObjectJSON["EmailEncodingKey"].ToString()));
                    _csentryChange.AttributeChanges.Add(AttributeChange.CreateAttributeAdd("LanguageLocaleKey", _importObjectJSON["LanguageLocaleKey"].ToString()));
                    _csentryChange.AttributeChanges.Add(AttributeChange.CreateAttributeAdd("LocaleSidKey", _importObjectJSON["LocaleSidKey"].ToString()));
                    _csentryChange.AttributeChanges.Add(AttributeChange.CreateAttributeAdd("ProfileId", _importObjectJSON["ProfileId"].ToString()));
                    _csentryChange.AttributeChanges.Add(AttributeChange.CreateAttributeAdd("TimeZoneSidKey", _importObjectJSON["TimeZoneSidKey"].ToString()));
                    _csentryChange.AttributeChanges.Add(AttributeChange.CreateAttributeAdd("UserPermissionsOfflineUser", false));
                    _csentryChange.AttributeChanges.Add(AttributeChange.CreateAttributeAdd("UserPermissionsMarketingUser", false));
                    _csentries.Add(_csentryChange);
                }
                var _importReturnInfo = new GetImportEntriesResults();
                _importReturnInfo.MoreToImport = false;
                _importReturnInfo.CSEntries = _csentries;
                return _importReturnInfo;

utils.GetContentsWithAccessTokenの中ではx-cdata-authtokenヘッダにAuthTokenをセットし、リソースURIにGETリクエストを投げているだけです。
                _httpClient.DefaultRequestHeaders.Add("x-cdata-authtoken", _accessToken);
                return await _httpClient.GetStringAsync(_url);


4.エクスポート処理関連(OpenExportConnection/PutExportEntriesメソッド)
 こちらは先のインポートとは逆で、実際にAPI Serverへユーザを出力(プロビジョニング)する処理です。
 OpenExportConnectionではインポート時と同じくリソースURIとAuthTokenを設定から取得し、PutExportEntriesでConnector Space内のプロビジョニング対象ユーザを取得してJSON形式に整形してリソースURIへPOSTを行います。
                foreach (CSEntryChange _csentryChange in _csentries)
                {
                    // build json to POST
                    var _attr = new Dictionary<string, string>();
                    // anchor attribute
                    _attr.Add("Username", _csentryChange.DN.ToString());
                    switch (_csentryChange.ObjectModificationType)
                    {
                        case ObjectModificationType.Add:
                        case ObjectModificationType.Update:
                        foreach (string _attribName in _csentryChange.ChangedAttributeNames)
                        {
                            var _attributeChange = _csentryChange.AttributeChanges[_attribName];
                            var _valueChanges = _attributeChange.ValueChanges;
                            if (_valueChanges != null)
                            {
                                foreach (var _valueChange in _valueChanges)
                                {
 if (_valueChange.ModificationType == ValueModificationType.Add)
 {
// new value
_attr.Add(_attribName, _valueChange.Value.ToString());
break;
}
 }
                                }
                            }
                            // build json
                            string _exportDataJSON = JsonConvert.SerializeObject(_attr);
                            string _exportResult = utils.PostContentsWithAccessToken(resource_uri, auth_token, _exportDataJSON, null);

                            _exportEntriesResults.CSEntryChangeResults.Add(
                                CSEntryChangeResult.Create(_csentryChange.Identifier,
_csentryChange.AttributeChanges,
MAExportError.Success));
                                break;
                            case ObjectModificationType.Delete:
                                // NOT Implemented
                                break;

先ほどと同じく、utils.PostContentsWithAccessTokenではAuthTokenをヘッダにつけてJSONをリソースURIへPOSTしています。

大きくはこれで終わりです。
後はビルドしてAzure AD Connectが読み込めるようにExtensionsフォルダ(初期値はC:\Program Files\Microsoft Azure AD Sync\Extensions)へDLLファイルをコピーしておきます。

参考までにソースは以下のレポジトリにあげておきます。
https://github.com/fujie/apiserver

◆管理エージェントの設定

*繰り返しになりますが、Azure AD ConnectへカスタムManagement Agentを組み込む場合、所定の手続きを踏まないとサポート対象外となります。今回は手元にちょうどあった環境を使ったのでAzure AD Connectを使いましたが、本番環境ではMIM(Microsoft Identity Manager)やExgen LDAP Managerなど汎用的なID管理ツールを使ってください。
次は、Azure AD ConnectのSynchronization Serviceに開発したECMA2エージェントを設定します。
コネクタの種類にExtensible Connectivity 2.0を選択し、DLLをロードします。



後は、先に示した画面の様に接続設定でリソースURI、AuthTokenを設定し、管理対象の属性を選択すれば管理エージェント自体の構成は終了です。

次は、Run Profileの設定です。これを設定しないとAzure AD Connectはインポート、同期、エクスポートの各処理を実行してくれません。また、プロファイルの名前をFull Import、Full Synchronization、Export、など決められた名称にしておかないと30分に一回の自動処理の流れに乗せてくれず処理がスタックするので命名は大事です。
尚、今回作った管理エージェントにはDelta Importの処理を実装していないので、プロファイルの定義は行えません。イベントログにDelta Importの定義が無いのでスキップしますよ~という警告が出ますが特に影響はないので無視します。


これでSynchronization Service側の設定は完了です。

◆同期ルールを定義する

後は実際のMetaverse上のデータと先ほど作成した管理エージェントのConnector Space上のエントリの属性のマッピング(同期ルール)の設定を行います。FIMやMIMではポータルでISR/OSRを定義してルールを設定するのですが、今回はAzure AD ConnectなのでSynchronization Rules Editorを使います。個人的にはFIM/MIMの設定画面よりもこのルールエディタの方が使いやすいので好きだったりします。

今回はプロビジョニングだけでいいので、アウトバウンド同期ルールを定義します。
DirectionはOutbound、Connectorは先ほど作成したAPI Serverを選択してAdd new ruleをクリックしてルールを作成します。(以下は作成後の画面)

LinkTypeにprovisionを選択することで新規にユーザを作成するためのルールとして扱われます。

MetaverseとConnector SpaceのユーザのマッチングはUserprincipalNameおよびUsernameを使うので、Join rulesは以下のように設定しています。

後は、本命の属性のマッピングです。
エンコードやロケール、プロファイルIDなどは面倒なので固定値を入れて、ユーザ固有の値だけMetaverseの情報をマッピングします。

これでSaveをするとすべての設定が完了です。

◆テスト

後は、ローカルのAD上にユーザを作成、Azure AD Connectの同期ジョブが動くと、Azure ADへのユーザ作成に加えて、API Serverを経由してSalesforce.comへもユーザが作成されます。

この辺りは前回も紹介したデモの動画を見てもらった方が早いと思います。



Azure AD Connectを使った関係で色々とトリッキーなこともしていますが、少なくともID管理システム側でSalesforce.com自体の存在を殆ど意識せずに構成をすることが出来ていることはご理解いただけるのではないでしょうか?
スキーマの設定についても動的に取得する様に管理エージェントを構成すれば、API Serverの先にあるのがSalesforce.comだろうがDynamics365だろうが基本設定を変える必要は全くなく、API Serverが上手くハンドリングをしてくれますので、従来の個別開発に比べて相当簡単に各種SaaSなどへのプロビジョニングを構成することが可能になります。

今後、クラウド・サービスの活用がますます増えてくると思われますので、既存のID管理システムを効率よく対応させていくための一つの選択肢として、API Serverのようなデータ統合プラットフォームを活用するのはアリかも知れませんね。

2017年7月29日土曜日

APIゲートウェイでSaaSのIDを簡単に管理する①

こんにちは、富士榮です。

先日、CDATAさんのイベントで、CDATA API Serverを利用してローカルADのアカウントをSalesforceへプロビジョニングする、というデモをお見せしました。



当日は時間も限られていたのと、あくまでAPI Serverの活用事例の一つとして紹介したので、あまり細かい話はしなかったのですが、ID管理をやっている方々からは、このデモの仕組みをもう少し細かく知りたい、という声も頂いたので少し細かくご紹介していきたいと思います。

当日の資料はこちら。




まずは、どのようなデモをお見せしたのか、動画を用意しているので最初にこちらをご覧ください。


使っている道具は、
・オンプレミスのActive Directory
・Azure AD Connect+REST Management Agent(カスタム)
・Azure Active Directory
・CDATA API Server 2017J
・Salesforce.com
です。
*ちなみにAzure AD ConnectへカスタムManagement Agentを組み込む場合、所定の手続きを踏まないとサポート対象外となります。今回は手元にちょうどあった環境を使ったのでAzure AD Connectを使いましたが、本番環境ではMIM(Microsoft Identity Manager)やExgen LDAP Managerなど汎用的なID管理ツールを使ってください。

こんなことをしなくてもAzure ADから直接Salesforce.comへプロビジョニングすればいいじゃん、という話はありますが、手軽にテナントが用意できるSaaSなのであえてSalesforce.comを選択しています。もちろんCDATA API Serverが接続用のドライバを持っているサービスであれば何でも構いません。


では、早速環境の解説をしていきます。

1.オンプレミスのActive DirectoryからAzure ADへの同期環境を構成する

これは、特に特殊なことはしていません。Office365向けのID基盤を構築する時と同様にAzure AD Connectを構成します。

ものすごいシンプルな構成です。ローカルADもexample.localですし。。。


2.Salesforce.comのテナントを用意し、Azure ADとシングルサインオンを設定する

これも良くあるものなので、何も細かいことは気にしません。
以下の手順で設定をしていきます。

・Salesforce.comへのカスタムドメインの追加

・Azure ADにSalesforce.comアプリの追加とシングルサインオン設定
 サインオンURL、EntityIDにSalesforce.comに設定したカスタムドメインを指定し、証明書をダウンロードしておきます。

・Salesforce.comへAzure ADとのシングルサインオン設定
 Azure ADからダウンロードした証明書のアップロード、Issuerの設定、SSO URI、SLO URI、Salesforce側のEntityIDの設定を行います。設定すべきURI等の情報はAzure ADの設定ページに手順を含め記載されています。

ここまででシングルサインオンは出来るようになります。


3.API ServerとSalesforce.comを接続する

API Serverはここから評価版のダウンロードができます。
http://www.cdata.com/jp/apiserver/

ただ、この本体にはSalesforce.comとの接続に使うドライバが同梱されていないので、別途Salesforce.comドライバ(ADO.NET版)をダウンロードします。
こちらも評価版があります。
http://www.cdata.com/jp/download/?f=ado

インストール自体は次へ、次への世界なので何も気にすることは無く、インストールが終わるAPI Serverが起動してきます。
ちなみにデモでは手元のWindows 10の端末で動かしています。

設定メニューより接続タブを開き、新規に接続先を追加します。

接続先アプリケーションとしてSalesforceを選択します。


Salesforce.comへの接続に使うユーザIDとパスワードを入れ、保存します。

ちなみに、信頼されていないネットワークから接続している場合は、セキュリティ・トークンの設定が求められることがあります。

このエラーが出た場合は、Salesforce.comに管理者でログインし個人の設定からセキュリティ・トークンをリセットし、メールで送られてくるセキュリティ・トークンをAPI Serverに設定します。



次に、Salesforce.comのユーザを管理しているテーブルをAPI Serverのリソースとして定義し、REST APIとして公開します。
同じく、設定メニューの中のリソースタブを開き、リソースを追加します。

データソースに先ほど定義したSalesforce.comへの接続を選択します。

ユーザ情報が格納されているテーブルの名前はその名の通りUserなので、選択して保存します。

Userテーブルのカラム一覧が自動的に取得されるので、そのまま保存します。

ここまででSalesforce.comのUserテーブルをAPI ServerがREST APIとして公開できました。
最後にAPI接続を行うためのAPI Server上のユーザを定義します。
ユーザタブを開き、追加をします。

ユーザ名と実行できるメソッドを指定して保存します。

保存すると認証に使うAuthトークンが表示されるのでユーザ名と合わせてメモしておきます。

これで設定は完了です。

接続確認としてユーザ一覧を取得してみます。
APIメニューを開き、先ほど作成したリソースを開くとエンドポイントとメソッドが出てきます。

今回はテストなのでユーザ一覧を取得するので、単純にUserエンドポイントをGETします。
BASIC認証がかかり、ダイアログが出てくるので先ほど作成したユーザとAuthトークンをID、パスワードとして入力します。

上手くいくと、ユーザ一覧がODATA形式で取得出来ます。

これで、API Server経由でSalesforce.com上のユーザを操作するための準備はすべて整いました。

後は、Azure AD ConnectからAPI Serverで公開したAPIを叩くだけです。
この部分は若干コードを書くので、次回詳細は解説したいと思います。

ということで今回はここまでです。

2017年4月5日水曜日

Office365管理者は要対応)UPNとProxyAddress重複オプションの強制変更がいよいよ開始

こんにちは、富士榮です。

以前紹介したAzure AD Connectでのディレクトリ同期時にUPNやProxyAddressが重複しないように自動的にサフィックスをつけてくれるオプションがいよいよ全テナントで強制的に有効になるようです。

 以前の紹介記事
 [Azure AD/Office365]管理者は要注意。ディレクトリ同期に関する仕様変更
 http://idmlab.eidentity.jp/2016/09/azure-adoffice365.html

簡単に内容をリマインドしておくと、複数のオンプレミスADから単一のAzure ADディレクトリへ同期したり、オンプレミスのUPN以外の属性(例えばメール属性)をAzure ADのUPNにマッピングするなど属性のユニーク性が担保されない環境で運用を行う際、ディレクトリ同期時にAzure AD側でUPNを自動的に「ユーザ名+4桁のランダム数値@デフォルトドメイン名」という様に変更する、というオプションが追加されました。
(UPN版とProxyAddress版の2つのオプションがあります)

このオプションが有効だとディレクトリ同期時の重複エラーを抑制することはできますが、勝手にUPNが書き換わってしまうのでオンプレミスのディレクトリの状態によっては若干困ることがあるので、昨年発表された段階では強制的に有効化されることはなく、しばらくは猶予期間が作られました。

しかし、この4月から遂に強制変更が開始されるようです。

4月1日前後に以下のメールが管理者宛てに来た場合は、メールに記載の日程から強制的に有効化されます。私のテナントの場合は4/19から強制有効化されるようです。
(以外と急でした・・・)

タイトル:New Identity Synchronization Feature Being Enabled - Duplicate Attribute Resiliency
差出人:MSOnlineServicesTeam@microsoftonline.com




急ぎ、自社のディレクトリ構成を確認してUPNが急に変わった~~~というようなパニックに陥らないように準備しておきましょう。




2016年11月25日金曜日

[Azure AD] Azure AD ConnectがようやくWindows Server 2016に対応

こんにちは、富士榮です。


これまでもWindows Server 2016にインストールして使ってしまっていたAzure AD Connectですが、11月版の更新(ビルド1.1.343.0)でやっと正式にWindows Server 2016への対応をしました。

Azure AD Connect version history
https://docs.microsoft.com/en-us/azure/active-directory/active-directory-aadconnect-version-history


細かいバグフィックスと同時に、SQL Server 2016への対応、およびAD FS 2016の管理についてもサポートする様になったので、新しい環境に導入する場合はこのバージョンを使っていきましょう。