使用 PowerShell 脚本进行 API 驱动的入站预配

本教程介绍如何使用 PowerShell 脚本实现Microsoft Entra ID API 驱动的入站预配。 使用本教程中的步骤,可以将包含 HR 数据的 CSV 文件转换为批量请求有效负载,并将其发送到 Microsoft Entra 预配 /bulkUpload API 终结点。 本文还提供了有关如何将同一集成模式与任何记录系统一起使用的指导。

集成方案

业务需求

您的记录系统会定期生成包含工作人员数据的 CSV 文件导出。 你希望实现一个集成功能,从 CSV 文件中读取数据,并在目标目录中自动配置用户帐户(对于混合用户使用本地 Active Directory,对于仅限云的用户使用 Microsoft Entra ID)。

实现要求

从实现的角度来看:

  • 你希望使用无人参与的 PowerShell 脚本从 CSV 文件导出中读取数据,并将其发送到入站预配 API 终结点。
  • 在 PowerShell 脚本中,你不希望实现在记录和目标目录系统之间比较标识数据的复杂逻辑。
  • 你希望使用 Microsoft Entra 预配服务应用 IT 托管预配规则,以便在目标目录中自动创建/更新/启用/禁用帐户(本地 Active Directory 或 Microsoft Entra ID)。

基于 PowerShell 的集成图形。

集成方案变体

虽然本教程使用 CSV 文件作为记录系统,但你可以自定义示例 PowerShell 脚本以从任何记录系统读取数据。 下面是企业集成方案变体的列表,其中可以使用 PowerShell 脚本实现 API 驱动的入站预配。

# 记录系统 有关使用 PowerShell 读取源数据的集成指南
1 数据库表 如果使用 Azure SQL 数据库或本地 SQL Server,可以使用 Read-SqlTableData cmdlet 读取存储在 SQL 数据库的表中的数据。 可以使用 Invoke-SqlCmd cmdlet 运行 Transact-SQL 或 XQuery 脚本。
如果使用的是 Oracle/MySQL/Postgres 数据库,则可以在 PowerShell 库中找到供应商发布的 PowerShell 模块或可用模块。 使用模块从数据库表读取数据。
2 LDAP 服务器 System.DirectoryServices.Protocols 使用 PowerShell 库中提供的 .NET API 或 LDAP 模块之一查询 LDAP 服务器。 了解 LDAP 架构和层次结构,以便从 LDAP 服务器检索用户数据。
3 公开 REST API 的任何系统 若要使用 PowerShell 从 REST API 终结点读取数据,可以使用模块中的 Microsoft.PowerShell.Utility cmdlet。 查看 REST API 的文档,了解所需的参数和标头、返回的格式以及它使用的身份验证方法。 然后,可以相应地调整 Invoke-RestMethod 命令。
4 公开 SOAP API 的任何系统 若要使用 PowerShell 从 SOAP API 终结点读取数据,可以使用模块中的 Microsoft.PowerShell.Management cmdlet。 查看 SOAP API 的文档,了解所需的参数和标头、返回的格式以及它使用的身份验证方法。 然后,可以相应地调整 New-WebServiceProxy 命令。

读取源数据后,应用预处理规则并将记录系统中的输出转换为批量请求,该请求可以发送到 Microsoft Entra 预配 bulkUpload API 终结点。

重要

若要与社区共享 PowerShell 集成脚本,请在 PowerShell 库中 发布该脚本,并在 GitHub 存储库 entra-id-inbound-provisioning上通知我们,以便我们可以添加引用。

如何使用本教程

Microsoft Entra 入站预配 GitHub 存储库中发布的 PowerShell 示例脚本自动执行多个任务。 它具有用于处理大型 CSV 文件的逻辑,并分块批量请求以在每个请求中发送 50 条记录。 下面介绍如何根据集成要求对其进行测试并对其进行自定义。

注释

提供示例 PowerShell 脚本“as-is”以供实现参考。 如果存在与脚本相关的问题,或者想要对其进行增强,请使用 GitHub 项目存储库

# 自动化任务 实施指南 高级自定义
1 从 CSV 文件读取工人数据。 下载 PowerShell 脚本。 它具有开箱即用的逻辑,可从任何 CSV 文件读取数据。 请参阅 CSV2SCIM PowerShell 使用情况详细信息 ,以熟悉此脚本的不同执行模式。 如果记录系统不同,请查看 集成方案变体 中提供的有关如何自定义 PowerShell 脚本的指南。
2 预处理数据并将其转换为 SCIM 格式。 默认情况下,PowerShell 脚本会将 CSV 文件中的每个记录转换为 SCIM Core 用户 + 企业用户表示形式。 按照“ 生成具有标准架构的批量请求有效负载 ”部分中的步骤熟悉此过程。 如果 CSV 文件具有不同的字段,请调整 AttributeMapping.psd 文件 以生成有效的 SCIM 用户。 还可以 使用自定义 SCIM 架构生成批量请求。 更新 PowerShell 脚本以包含任何自定义 CSV 数据验证逻辑。
3 使用证书进行身份验证到 Microsoft Entra ID。 创建能够访问 入站配置 API 的服务主体。 请参阅“ 为服务主体身份验证配置客户端证书 ”部分中的步骤,了解如何使用客户端证书进行身份验证。 如果要使用托管标识而不是服务主体进行身份验证,请查看示例脚本中的用法 Connect-MgGraph 并将其更新为使用 托管标识
4 在本地 Active Directory 或 Microsoft Entra ID 中预配帐户。 配置 API 驱动的入站预配应用。 这会生成唯一 /bulkUpload API 终结点。 若要使用具有基于证书的身份验证的服务主体运行脚本,请参阅“ 使用客户端证书身份验证上传批量请求有效负载”部分中的步骤。 根据集成要求验证属性流并自定义属性映射。 如果计划 对自定义 SCIM 架构使用批量请求,请 扩展预配应用架构 以包含自定义 SCIM 架构元素。
5 扫描预配日志并重试预配失败的记录。 请参阅“ 获取最新同步周期的预配日志 ”部分中的步骤,了解如何提取和分析预配日志数据。 确定失败的用户记录,并将其包含在下一个上传周期中。 -
6 将基于 PowerShell 的自动化部署到生产环境。 验证 API 驱动的预配流并自定义 PowerShell 脚本以满足要求后,可以在 Azure 自动化中将自动化部署为 PowerShell 工作流 Runbook ,或部署为 计划在 Windows 服务器上运行的服务器进程。 -

下载 PowerShell 脚本

  1. 访问 GitHub 存储库 entra-id-inbound-provisioning
  2. 使用 “代码>克隆 ”或“ 代码>下载 ZIP ”选项将此存储库的内容复制到本地文件夹中。
  3. 导航到 PowerShell/CSV2SCIM文件夹。 它具有以下目录结构:
    • 源代码 (src)
      • CSV2SCIM.ps1(主脚本)
      • ScimSchemaRepresentations (包含用于验证 AttributeMapping.psd1 文件的标准 SCIM 架构定义的文件夹)
        • EnterpriseUser.json、Group.json、Schema.json、User.json
    • 样品
      • AttributeMapping.psd1 (CSV 文件中的列到标准 SCIM 属性的示例映射)
      • csv-with-2-records.csv(包含两条记录的示例 CSV 文件)
      • csv-with-1000-records.csv(包含 1000 条记录的示例 CSV 文件)
      • Test-ScriptCommands.ps1(示例用法命令)
      • UseClientCertificate.ps1(用于生成自签名证书的脚本并将其作为服务主体凭据上传到 OAuth 流中)
      • Sample1 (包含有关如何将 CSV 文件列映射到 SCIM 标准属性的更多示例的文件夹。如果为员工、承包商、实习生获取不同的 CSV 文件,则可以为每个实体创建单独的 AttributeMapping.psd1 文件。
  4. 下载并安装最新版本的 PowerShell。
  5. 运行以下命令以启用远程签名脚本的执行:
    set-executionpolicy remotesigned
    
  6. 安装以下必备模块:
    Install-Module -Name Microsoft.Graph.Applications,Microsoft.Graph.Reports
    

使用标准架构生成批量请求有效负载

本部分介绍如何从 CSV 文件生成具有标准 SCIM 核心用户和企业用户属性的批量请求有效负载。 为了说明该过程,让我们使用 CSV 文件 Samples/csv-with-2-records.csv

  1. 在记事本++ 或 Excel 中打开 CSV 文件 Samples/csv-with-2-records.csv ,检查文件中存在的列。 Excel 中列的屏幕截图。

  2. 在记事本++ 或源代码编辑器(如 Visual Studio Code)中,打开 PowerShell 数据文件 Samples/AttributeMapping.psd1 ,以便将 CSV 文件列映射到 SCIM 标准架构属性。 开箱即用的文件已预先配置了 CSV 文件列与相应的 SCIM 架构属性之间的映射。

        @{
        externalId   = 'WorkerID'
        name         = @{
            familyName = 'LastName'
            givenName  = 'FirstName'
        }
        active       = { $_.'WorkerStatus' -eq 'Active' }
        userName     = 'UserID'
        displayName  = 'FullName'
        nickName     = 'UserID'
        userType     = 'WorkerType'
        title        = 'JobTitle'
        addresses    = @(
            @{
                type          = { 'work' }
                streetAddress = 'StreetAddress'
                locality      = 'City'
                postalCode    = 'ZipCode'
                country       = 'CountryCode'
            }
        )
        phoneNumbers = @(
            @{
                type  = { 'work' }
                value = 'OfficePhone'
            }
        )
        "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User" = @{
            employeeNumber = 'WorkerID'
            costCenter     = 'CostCenter'
            organization   = 'Company'
            division       = 'Division'
            department     = 'Department'
            manager        = @{
                value = 'ManagerID'
            }
        }
    }
    
  3. 打开 PowerShell 并更改为 目录 CSV2SCIM\src

  4. 运行以下命令来初始化 AttributeMapping 变量。

    $AttributeMapping = Import-PowerShellDataFile '..\Samples\AttributeMapping.psd1'
    
  5. 运行以下命令,验证文件是否 AttributeMapping 具有有效的 SCIM 架构属性。 如果验证成功,此命令将返回 True

    .\CSV2SCIM.ps1 -Path '..\Samples\csv-with-2-records.csv' -AttributeMapping $AttributeMapping -ValidateAttributeMapping
    
  6. 假设AttributeMapping文件有一个无效的SCIM属性,名为userId,那么ValidateAttributeMapping模式会显示以下错误。

    映射错误的屏幕截图。

  7. 验证 AttributeMapping 文件是否有效后,运行以下命令,在包含 CSV 文件中存在的两条记录的文件中 BulkRequestPayload.json 生成批量请求。

    .\CSV2SCIM.ps1 -Path '..\Samples\csv-with-2-records.csv' -AttributeMapping $AttributeMapping > BulkRequestPayload.json
    
  8. 可以打开文件 BulkRequestPayload.json 的内容,验证 SCIM 属性是否根据文件中 AttributeMapping.psd1定义的映射进行设置。

  9. 可以使用 cURL 将上面生成的文件发布到与预配应用关联的 /bulkUpload API 终结点 as-is。 参考:

  10. 若要使用同一 PowerShell 脚本将生成的有效负载直接上传到 API 终结点,请参阅下一部分。

为服务主体身份验证配置客户端证书

注释

此处的说明演示如何生成自签名证书。 默认情况下,自签名证书不受信任,难以维护。 此外,它们可能使用过时的哈希和密码套件,这些套件可能不强。 为了提高安全性,请购买由知名证书颁发机构签名的证书。

  1. 运行以下 PowerShell 脚本以生成新的自签名证书。 如果已购买由已知证书颁发机构签名的证书,则可以跳过此步骤。
    $ClientCertificate = New-SelfSignedCertificate -Subject 'CN=CSV2SCIM' -KeyExportPolicy 'NonExportable' -CertStoreLocation Cert:\CurrentUser\My
    $ThumbPrint = $ClientCertificate.ThumbPrint
    
    生成的证书存储为 Current User\Personal\Certificates。 可以使用 “控制面板>管理用户证书 ”选项查看它。
  2. 若要将此证书与有效的服务主体相关联,请以应用程序管理员身份登录到 Microsoft Entra 管理中心。
  3. 应用注册下打开您配置的服务主体
  4. “概述”边栏选项卡中复制对象 ID。 使用该值替换字符串 <AppObjectId>。 复制 应用程序(客户端)ID。稍后我们将使用它,并被引用为 <AppClientId>
  5. 运行以下命令,将证书上传到已注册的服务主体。
    Connect-MgGraph -Environment China -ClientId 'YOUR_CLIENT_ID' -TenantId 'YOUR_TENANT_ID' -Scopes "Application.ReadWrite.All"
    Update-MgApplication -ApplicationId '<AppObjectId>' -KeyCredentials @{
       Type = "AsymmetricX509Cert"
       Usage = "Verify"
       Key = $ClientCertificate.RawData
    }
    
    应在已注册应用的 “证书和机密 ”边栏选项卡下看到证书。 客户端证书的屏幕截图。
  6. 将以下两个 应用程序 权限范围添加到服务主体应用: Application.Read.AllSynchronization.Read.All。 为了让 PowerShell 脚本通过ServicePrincipalId查找预配应用并获取预配JobId,需要这些设置。

使用客户端证书身份验证上传批量请求有效负载

本部分介绍如何使用受信任的客户端证书将生成的批量请求有效负载发送到入站预配 API 终结点。

  1. 打开 配置的 API 驱动的预配应用。 ServicePrincipalId复制与您的预配应用相关联的“预配应用>属性>对象 ID”

    对象 ID 的屏幕截图。

  2. 运行以下命令,并为ServicePrincipalIdClientIdTenantId提供正确的值。

    $ClientCertificate = Get-ChildItem -Path cert:\CurrentUser\my\ | Where-Object {$_.Subject -eq "CN=CSV2SCIM"}  
    $ThumbPrint = $ClientCertificate.ThumbPrint
    
    .\CSV2SCIM.ps1 -Path '..\Samples\csv-with-2-records.csv' -AttributeMapping $AttributeMapping -TenantId "contoso.partner.onmschina.cn" -ServicePrincipalId "<ProvisioningAppObjectId>" -ClientId "<AppClientId>" -ClientCertificate (Get-ChildItem Cert:\CurrentUser\My\$ThumbPrint)
    
  3. 访问预配应用的 “预配日志 ”边栏选项卡,验证上述请求的处理情况。

使用自定义 SCIM 架构生成批量请求

本部分介绍如何使用由 CSV 文件中的字段组成的自定义 SCIM 架构命名空间生成批量请求。

  1. 在记事本++ 或源代码编辑器(如 Visual Studio Code)中,打开 PowerShell 数据文件 Samples/AttributeMapping.psd1 ,以便将 CSV 文件列映射到 SCIM 标准架构属性。 开箱即用的文件已预先配置了 CSV 文件列与相应的 SCIM 架构属性之间的映射。

  2. 打开 PowerShell 并更改为 目录 CSV2SCIM\src

  3. 运行以下命令来初始化 AttributeMapping 变量。

    $AttributeMapping = Import-PowerShellDataFile '..\Samples\AttributeMapping.psd1'
    
  4. 运行以下命令,验证文件是否 AttributeMapping 具有有效的 SCIM 架构属性。 如果验证成功,此命令将返回 True

    .\CSV2SCIM.ps1 -Path '..\Samples\csv-with-2-records.csv' -AttributeMapping $AttributeMapping -ValidateAttributeMapping
    
  5. 除了 SCIM 核心用户和企业用户属性之外,若要获取自定义 SCIM 架构命名空间 urn:ietf:params:scim:schemas:extension:contoso:1.0:User下所有 CSV 字段的平面列表,请运行以下命令。

     .\CSV2SCIM.ps1 -Path '..\Samples\csv-with-2-records.csv' -AttributeMapping $AttributeMapping -ScimSchemaNamespace "urn:ietf:params:scim:schemas:extension:contoso:1.0:User"  > BulkRequestPayloadWithCustomNamespace.json
    

    CSV 字段将显示在自定义 SCIM 架构命名空间下。 自定义架构下的用户详细信息的屏幕截图。

扩展预配作业架构

HR 团队发送的数据文件通常包含更多属性,这些属性在标准 SCIM 架构中没有直接表示形式。 若要表示此类属性,建议创建 SCIM 扩展架构并在此命名空间下添加属性。

CSV2SCIM脚本提供了一种执行模式,该模式调用 UpdateSchema 该模式可读取 CSV 文件中的所有列,在扩展架构命名空间下添加这些列,并更新预配应用架构。

注释

如果预配应用架构中已存在属性扩展,则此模式只会发出一条警告,指出属性扩展已存在。 因此,如果在 UpdateSchema 模式下运行CSV2SCIM脚本时,如果新字段添加到 CSV 文件,并且想要将其添加为扩展,则不会出现问题。

为了说明该过程,我们将使用Samples/csv-with-2-records.csv文件夹中存在的 CSV 文件

  1. 在记事本、Excel 或 TextPad 中打开 CSV 文件 Samples/csv-with-2-records.csv ,检查文件中存在的列。

    如何检查 CSV 列的屏幕截图。

  2. 运行下面的命令:

    .\CSV2SCIM.ps1 -Path '..\Samples\csv-with-2-records.csv' -UpdateSchema -ServicePrincipalId <servicePrincipalId> -TenantId "contoso.partner.onmschina.cn" -ScimSchemaNamespace "urn:ietf:params:scim:schemas:extension:contoso:1.0:User"
    
  3. 可以通过打开“属性映射”页并访问“高级”选项下的“API”选项的“编辑属性列表”来验证预配应用架构的更新。

  4. 属性列表显示新命名空间下的属性。

获取最新同步周期的预配日志

发送批量请求后,可以查询Microsoft Entra ID 处理的最新同步周期的日志。 可以使用 PowerShell 脚本检索同步统计信息和处理详细信息,并将其保存以供分析。

  1. 若要查看控制台上的日志详细信息和同步统计信息,请运行以下命令:

    .\CSV2SCIM.ps1 -ServicePrincipalId <servicePrincipalId> -TenantId "contoso.partner.onmschina.cn" -GetPreviousCycleLogs -NumberOfCycles 1
    

    同步统计信息的屏幕截图。

    注释

    NumberOfCycles 默认为 1。 指定用于检索更多同步周期的数字。

  2. 若要查看主机上的同步统计信息并将日志详细信息保存到变量,请运行以下命令:

    $logs=.\CSV2SCIM.ps1 -ServicePrincipalId <servicePrincipalId> -TenantId "contoso.partner.onmschina.cn" -GetPreviousCycleLogs
    

    若要使用客户端证书身份验证来运行命令,请提供 ServicePrincipalIdClientIdTenantId 的正确值,然后执行命令:

    $ClientCertificate = Get-ChildItem -Path cert:\CurrentUser\my\ | Where-Object {$_.Subject -eq "CN=CSV2SCIM"}  
    $ThumbPrint = $ClientCertificate.ThumbPrint
    
    $logs=.\CSV2SCIM.ps1 -ServicePrincipalId "<ProvisioningAppObjectId>" -TenantId "contoso.partner.onmschina.cn" -ClientId "<AppClientId>" -ClientCertificate (Get-ChildItem Cert:\CurrentUser\My\$ThumbPrint) -GetPreviousCycleLogs -NumberOfCycles 1
    
    • 若要查看特定记录的详细信息,可以循环访问集合或选择该记录的特定索引,例如: $logs[0]

      所选索引的屏幕截图。

    • 我们还可以使用 where-object 语句通过 sourceID 或 DisplayName 搜索特定记录。 在 ProvisioningLogs 属性中,可以找到针对该特定记录执行的操作的所有详细信息。

      $user = $logs | where sourceId -eq '1222'
      $user.ProvisioningLogs | fl
      

      预配日志的屏幕截图。

    • 我们可以在ModifiedProperties属性上看到受影响用户的特定属性。 $user.ProvisioningLogs.ModifiedProperties

      属性的屏幕截图。

附录

CSV2SCIM PowerShell 使用情况详细信息

下面是CSV2SCIM PowerShell 脚本接受的命令行参数的列表。

PS > CSV2SCIM.ps1 -Path <path-to-csv-file> 
[-ScimSchemaNamespace <customSCIMSchemaNamespace>] 
[-AttributeMapping $AttributeMapping] 
[-ServicePrincipalId <spn-guid>] 
[-ValidateAttributeMapping]
[-UpdateSchema]
[-ClientId <client-id>]
[-ClientCertificate <certificate-object>]
[-RestartService]

注释

AttributeMapping ValidateAttributeMapping命令行参数是指 CSV 列属性与标准 SCIM 架构元素的映射。 它不指在源 SCIM 架构元素与目标 Microsoft Entra / 本地 Active Directory 属性之间的 Microsoft Entra 管理中心预配应用中执行的属性映射。

参数 说明 处理备注
路径 CSV 文件的完整或相对路径。 例如:.\Samples\csv-with-1000-records.csv 必需:是
ScimSchemaNamespace 用于将 CSV 文件中的所有列作为属于特定命名空间的自定义 SCIM 属性发送的自定义 SCIM 架构命名空间。 例如: urn:ietf:params:scim:schemas:extension:csv:1.0:User 必需:仅当需要执行以下操作时:
- 更新预配应用程序架构,或
需要在有效载荷中包含自定义 SCIM 属性。
属性映射 指向 PowerShell 数据(.psd1 扩展)文件,该文件将 CSV 文件中的列映射到 SCIM Core User 和企业用户属性。
请参阅示例: CSV2SCIM脚本的AttributeMapping.psd 文件
例如: powershell $AttributeMapping = Import-PowerShellDataFile '.\Samples\AttributeMapping.psd1'`-AttributeMapping $AttributeMapping
必需:是
只有在使用 UpdateSchema 开关选项时,才不需要指定它。
ValidateAttributeMapping 使用此 Switch 标志验证 AttributeMapping 文件是否包含符合 SCIM Core 和企业用户架构的属性。 不强制:建议使用它来确保合规性。
ServicePrincipalId 预配应用的服务主体 ID 的 GUID 值,可以从预配应用>属性>对象 ID 中检索到 必需:仅当想要:
- 更新预配应用架构,或
向 API 终结点发送生成的批量请求。
UpdateSchema 使用此开关指示脚本读取 CSV 列,并将其添加为预配应用架构中的自定义 SCIM 属性。
ClientId 要用于 OAuth 认证流程的 Microsoft Entra 注册的应用程序的客户端 ID。 此应用必须具有有效的证书凭据。 必需:仅在执行基于证书的身份验证时。
客户端证书 在 OAuth 流期间使用的客户端身份验证证书。 必需:仅在执行基于证书的身份验证时。
获取上一个周期日志 获取最新同步周期的预配日志。
循环次数 指定应检索多少个同步周期。 此值默认为 1。
RestartService 使用此选项,脚本在上传数据之前暂时暂停预配作业,它会上传数据,然后再次启动作业,以确保立即处理有效负载。 仅在测试期间使用此选项。

AttributeMapping.psd 文件

此文件用于将 CSV 文件中的列映射到标准 SCIM Core 用户和企业用户属性架构元素。 该文件还会生成 CSV 文件内容的相应表示,作为批量请求的有效负载。

在下一个示例中,我们将 CSV 文件中的以下列映射到其对应的 SCIM Core User 和企业用户属性。

映射属性的 CSV 列的屏幕截图。

    @{
    externalId   = 'WorkerID'
    name         = @{
        familyName = 'LastName'
        givenName  = 'FirstName'
    }
    active       = { $_.'WorkerStatus' -eq 'Active' }
    userName     = 'UserID'
    displayName  = 'FullName'
    nickName     = 'UserID'
    userType     = 'WorkerType'
    title        = 'JobTitle'
    addresses    = @(
        @{
            type          = { 'work' }
            streetAddress = 'StreetAddress'
            locality      = 'City'
            postalCode    = 'ZipCode'
            country       = 'CountryCode'
        }
    )
    phoneNumbers = @(
        @{
            type  = { 'work' }
            value = 'OfficePhone'
        }
    )
    "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User" = @{
        employeeNumber = 'WorkerID'
        costCenter     = 'CostCenter'
        organization   = 'Company'
        division       = 'Division'
        department     = 'Department'
        manager        = @{
            value = 'ManagerID'
        }
    }
}

后续步骤