diff --git a/src/OKTACore/Configuration - PowershellRestAPI(Okta).xml b/src/OKTACore/Configuration - PowershellRestAPI(Okta).xml new file mode 100644 index 0000000..cd555eb --- /dev/null +++ b/src/OKTACore/Configuration - PowershellRestAPI(Okta).xml @@ -0,0 +1,1979 @@ + + + + + + + + Distinguished Name Style + DropDown + Capabilities + 1 + false + None + + + Export Type + DropDown + Capabilities + 1 + false + ObjectReplace + + + Data Normalization + DropDown + Capabilities + 1 + false + None + + + Object Confirmation + DropDown + Capabilities + 1 + false + NoAddAndDeleteConfirmation + + + Use DN As Anchor (Only LDAP style DN) + CheckBox + Capabilities + 1 + false + 0 + + + Concurrent Operations Of Several Connectors + CheckBox + Capabilities + 1 + false + 0 + + + Partitions (Only LDAP style DN) + CheckBox + Capabilities + 1 + false + 0 + + + Hierarchy (Only LDAP style DN) + CheckBox + Capabilities + 1 + false + 0 + + + Enable Import + CheckBox + Capabilities + 1 + false + 1 + + + Enable Delta Import + CheckBox + Capabilities + 1 + false + 0 + + + Enable Export + CheckBox + Capabilities + 1 + false + 1 + + + Enable Full Export + CheckBox + Capabilities + 1 + false + 1 + + + No Reference Values In First Export Pass + CheckBox + Capabilities + 1 + false + 0 + + + Enable Object Rename + CheckBox + Capabilities + 1 + false + 0 + + + Delete-Add As Replace + CheckBox + Capabilities + 1 + false + 0 + + + Enable Password operations + CheckBox + Capabilities + 1 + false + 0 + + + Enable Export Password In First Pass + CheckBox + Capabilities + 1 + false + 0 + + + Server + String + Connectivity + 1 + false + + + + Domain + String + Connectivity + 1 + false + + + + User + String + Connectivity + 1 + false + APIKey + + + Password + EncryptedString + Connectivity + 1 + true + + + + Impersonate Connector Account + CheckBox + Connectivity + 1 + false + 0 + + + Load User Profile When Impersonating + CheckBox + Connectivity + 1 + false + 0 + + + Logon Type When Impersonating + DropDown + Connectivity + 1 + false + None + + + Signed Scripts Only + CheckBox + Connectivity + 1 + false + 0 + + + Common Module Script Name (with extension) + String + Connectivity + 1 + false + CommonModule.psm1 + + + Common Module Script + Text + Connectivity + 1 + false + Set-PSDebug -Strict + +function Enter-Script +{ + <# + .Synopsis + Writes the Versbose message saying specified script execution started. + .Description + Writes the Versbose message saying specified script execution started. + Also clear the $Error variable. + #> + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string] + $ScriptType, + [Parameter(Mandatory = $false)] + [ValidateNotNull()] + [System.Collections.ArrayList] + $ErrorObject + ) + + process + { + Write-Verbose "$Global:ConnectorName - $ScriptType Script: Execution Started..." + if ($ErrorObject) + { + $ErrorObject.Clear() + } + } +} + +function Exit-Script +{ + <# + .Synopsis + Checks $Error variable for any Errors. Writes the Versbose message saying specified script execution sucessfully completed. + .Description + Checks $Error variable for any Errors. Writes the Versbose message saying specified script execution sucessfully completed. + Throws an exception if $Error is present + #> + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string] + $ScriptType, + [Parameter(Mandatory = $false)] + [ValidateNotNull()] + [System.Collections.ArrayList] + $ErrorObject, + [Parameter(Mandatory = $false)] + [switch] + $SuppressErrorCheck, + [Parameter(Mandatory = $false)] + [type] + $ExceptionRaisedOnErrorCheck + ) + + process + { + if (!$SuppressErrorCheck -and $ErrorObject -and $ErrorObject.Count -ne 0) + { + # Take the first one otherwise you get "An error occurred while enumerating through a collection: Collection was modified; enumeration operation may not execute.." + # Seems like a bug in Remote PSH + $errorMessage = $ErrorObject[0] # | Out-String -ErrorAction SilentlyContinue + + if ($ExceptionRaisedOnErrorCheck -eq $null) + { + $ExceptionRaisedOnErrorCheck = [Microsoft.MetadirectoryServices.ExtensibleExtensionException] + } + + $ErrorObject.Clear() + + throw $errorMessage -as $ExceptionRaisedOnErrorCheck + } + + Write-Verbose "$Global:ConnectorName - $ScriptType Script: Execution Completed." + } +} + +function Get-ExtensionsDirectory +{ + <# + .Synopsis + Gets the path of the "Extensions" folder. + .Description + Gets the path of the "Extensions" folder. + #> + [CmdletBinding()] + [OutputType([string])] + param( + ) + + process + { + $scriptDir = "C:\\Program Files\\Microsoft ECMA2Host\\Service\\ECMA" + + return $scriptDir + } +} + +function ConvertFrom-SchemaXml +{ + <# + .Synopsis + Converts a connector schema defined in a xml file into a "Microsoft.MetadirectoryServices.Schema" object. + .Description + Converts a connector schema defined in a xml file into a "Microsoft.MetadirectoryServices.Schema" object. + .Example + ConvertFrom-SchemaXml -SchemaXml "Schema.xml" + #> + + [CmdletBinding()] + [OutputType([Microsoft.MetadirectoryServices.Schema])] + param( + [Parameter(Mandatory = $true)] + [ValidateScript({ Test-Path $_ -PathType "Leaf" })] + [string] + $SchemaXml + ) + + process + { + $x = [xml](Get-Content $SchemaXml) + + $schema = [Microsoft.MetadirectoryServices.Schema]::Create() + + foreach ($t in $x.Schema.Types.SchemaType) + { + $lockAnchorDefinition = $true + + if ($t.LockAnchorDefinition -eq "0") + { + $lockAnchorDefinition = $false + } + + $schemaType = [Microsoft.MetadirectoryServices.SchemaType]::Create($t.Name,$lockAnchorDefinition) + + if ($t.GetElementsByTagName("PossibleDNComponentsForProvisioning").Count -gt 0) + { + foreach ($c in $t.PossibleDNComponentsForProvisioning) + { + $schemaType.PossibleDNComponentsForProvisioning.Add($c) + } + } + + foreach ($a in $t.Attributes.SchemaAttribute) + { + if ($a.IsAnchor -eq 1) + { + $schemaType.Attributes.Add([Microsoft.MetadirectoryServices.SchemaAttribute]::CreateAnchorAttribute($a.Name,$a.DataType,$a.AllowedAttributeOperation)) + } + elseif ($a.IsMultiValued -eq 1) + { + $schemaType.Attributes.Add([Microsoft.MetadirectoryServices.SchemaAttribute]::CreateMultiValuedAttribute($a.Name,$a.DataType,$a.AllowedAttributeOperation)) + } + else + { + $schemaType.Attributes.Add([Microsoft.MetadirectoryServices.SchemaAttribute]::CreateSingleValuedAttribute($a.Name,$a.DataType,$a.AllowedAttributeOperation)) + } + } + + $schema.Types.Add($schemaType) + } + + return $schema + } +} + + +function Get-xADSyncPSConnectorSetting +{ + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [ValidateNotNull()] + [Alias('InputObject')] + [System.Collections.ObjectModel.KeyedCollection[string, Microsoft.MetadirectoryServices.ConfigParameter]] + $ConfigurationParameters, + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [string] + $Name, + [Parameter(Mandatory = $true)] + [ValidateSet('Global','Partition','RunStep')] + [string] + $Scope, + $DefaultValue + ) + process + { + try + { + $scopedName = '{0}_{1}' -f $Name,$Scope + + if ($ConfigurationParameters[$scopedName].Value) + { + return $ConfigurationParameters[$scopedName].Value + } + elseif ($PSBoundParameters.ContainsKey('DefaultValue')) + { + return $DefaultValue + } + else + { + return $null + } + } + catch [System.Collections.Generic.KeyNotFoundException] + { + # if they gave us a default, go ahead and return it + if ($PSBoundParameters.ContainsKey('DefaultValue')) + { + return $DefaultValue + } + else + { + throw + } + } + } +} + +function Get-xADSyncPSConnectorFolder +{ + [CmdletBinding()] + [OutputType([string])] + param( + [Parameter(Mandatory = $true,Position = 0)] + [ValidateSet('ManagementAgent','Extensions')] + [string] + $Folder + ) + + switch ($Folder) + { + 'ManagementAgent' + { + return [Microsoft.MetadirectoryServices.MAUtils]::MAFolder + } + 'Extensions' + { + return [Microsoft.MetadirectoryServices.Utils]::ExtensionsDirectory + } + default + { + throw "Folder '$Folder' is not supported" + } + } +} + +#region Schema Helpers +function New-xADSyncPSConnectorSchema +{ + [CmdletBinding()] + [OutputType([Microsoft.MetadirectoryServices.Schema])] + param() + + return [Microsoft.MetadirectoryServices.Schema]::Create() +} + +function New-xADSyncPSConnectorSchemaType +{ + [CmdletBinding()] + [OutputType([Microsoft.MetadirectoryServices.SchemaType])] + param( + [ValidateNotNullOrEmpty()] + [string] + $Name, + [switch] + $LockAnchorAttributeDefinition + ) + + return [Microsoft.MetadirectoryServices.SchemaType]::Create($Name,$LockAnchorAttributeDefinition.ToBool()) +} + +function Add-xADSyncPSConnectorSchemaAttribute +{ + [CmdletBinding(DefaultParameterSetName = 'Singlevalued')] + param( + [Parameter(Mandatory = $true,ValueFromPipeline = $true)] + [Microsoft.MetadirectoryServices.SchemaType] + [ValidateNotNull()] + $InputObject, + [ValidateNotNullOrEmpty()] + [string] + [Parameter(Mandatory = $true,ParameterSetName = 'Anchor')] + [Parameter(Mandatory = $true,ParameterSetName = 'Multivalued')] + [Parameter(Mandatory = $true,ParameterSetName = 'Singlevalued')] + $Name, + [Parameter(ParameterSetName = 'Anchor')] + [switch] + $Anchor, + [Parameter(ParameterSetName = 'Multivalued')] + [switch] + $Multivalued, + [Parameter(Mandatory = $true,ParameterSetName = 'Anchor')] + [Parameter(Mandatory = $true,ParameterSetName = 'Multivalued')] + [Parameter(Mandatory = $true,ParameterSetName = 'Singlevalued')] + [ValidateSet('Binary','Boolean','Integer','Reference','String')] + [string] + $DataType, + [Parameter(Mandatory = $true,ParameterSetName = 'Anchor')] + [Parameter(Mandatory = $true,ParameterSetName = 'Multivalued')] + [Parameter(Mandatory = $true,ParameterSetName = 'Singlevalued')] + [ValidateSet('ImportOnly','ExportOnly','ImportExport')] + [string] + $SupportedOperation + ) + + process + { + switch ($PSCmdlet.ParameterSetName) + { + 'Singlevalued' + { + $InputObject.Attributes.Add([Microsoft.MetadirectoryServices.SchemaAttribute]::CreateSingleValuedAttribute($Name,$DataType,$SupportedOperation)) + } + 'Multivalued' + { + if ($Multivalued.ToBool() -eq $true) + { + $InputObject.Attributes.Add([Microsoft.MetadirectoryServices.SchemaAttribute]::CreateMultiValuedAttribute($Name,$DataType,$SupportedOperation)) + } + else + { + $InputObject.Attributes.Add([Microsoft.MetadirectoryServices.SchemaAttribute]::CreateSingleValuedAttribute($Name,$DataType,$SupportedOperation)) + } + } + 'Anchor' + { + if ($Anchor.ToBool() -eq $true) + { + $InputObject.Attributes.Add([Microsoft.MetadirectoryServices.SchemaAttribute]::CreateAnchorAttribute($Name,$DataType,$SupportedOperation)) + } + else + { + $InputObject.Attributes.Add([Microsoft.MetadirectoryServices.SchemaAttribute]::CreateSingleValuedAttribute($Name,$DataType,$SupportedOperation)) + } + } + default + { + throw "Parameter set '$($PSCmdlet.ParameterSetName)' is not supported" + } + } + } +} +#endregion + +#region Partition Helpers +function New-FIMPSConnectorPartition +{ + [CmdletBinding()] + [OutputType([Microsoft.MetaDirectoryServices.Partition])] + param( + [Parameter(Mandatory = $true)] + [guid] + $Identifier, + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [string] + $DistinguishedName, + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [string] + $DisplayName + ) + + if ($PSBoundParameters.ContainsKey('DisplayName')) + { + return [Microsoft.MetadirectoryServices.Partition]::Create($Identifier,$DistinguishedName,$DisplayName) + } + else + { + return [Microsoft.MetadirectoryServices.Partition]::Create($Identifier,$DistinguishedName) + } +} +#endregion +function New-xADSyncPSConnectorHierarchyNode +{ + [CmdletBinding()] + [OutputType([Microsoft.MetadirectoryServices.HierarchyNode])] + param( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [string] + $DistinguishedName, + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [string] + $DisplayName + ) + + return [Microsoft.MetadirectoryServices.HierarchyNode]::Create($DistinguishedName,$DisplayName) +} +#region Hierarchy Helpers + +#endregion + +#region Import Helpers +function New-xADSyncPSConnectorCSEntryChange +{ + [CmdletBinding()] + [OutputType([Microsoft.MetadirectoryServices.CSEntryChange])] + param( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [string] + $ObjectType, + [Parameter(Mandatory = $true)] + [ValidateSet('Add','Delete','Update','Replace','None')] + [string] + $ModificationType, + [ValidateNotNullOrEmpty()] + [Alias('DistinguishedName')] + [string] + $DN, + [ValidateNotNullOrEmpty()] + [Alias('RelativeDistinguishedName')] + [string] + $RDN + ) + + $csEntry = [Microsoft.MetadirectoryServices.CSEntryChange]::Create() + $csEntry.ObjectModificationType = $ModificationType + $csEntry.ObjectType = $ObjectType + + if ($PSBoundParameters.ContainsKey('DN')) + { + $csEntry.DN = $DN + } + + if ($PSBoundParameters.ContainsKey('RDN')) + { + $csEntry.RDN = $RDN + } + + Write-Output $csEntry +} + +function Add-xADSyncPSConnectorCSAttribute +{ + [CmdletBinding()] + param( + [Parameter(Mandatory = $true,ValueFromPipeline = $true)] + [Microsoft.MetadirectoryServices.CSEntryChange] + [ValidateNotNull()] + $InputObject, + [Parameter(Mandatory = $true)] + [ValidateSet('Add','Update','Delete','Replace','Rename')] + [string] + $ModificationType, + [ValidateNotNullOrEmpty()] + [string] + $Name, + $Value + ) + + process + { + if ($ModificationType -ne 'Rename' -and $Name -eq $null) + { + throw 'Name parameter is required' + } + + if ($ModificationType -ne 'Delete' -and $Value -eq $null) + { + throw 'Value parameter is required' + } + + switch ($ModificationType) + { + 'Add' + { + $InputObject.AttributeChanges.Add([Microsoft.MetadirectoryServices.AttributeChange]::CreateAttributeAdd($Name,$Value)) + } + 'Update' + { + $InputObject.AttributeChanges.Add([Microsoft.MetadirectoryServices.AttributeChange]::CreateAttributeUpdate($Name,$Value)) + } + 'Delete' + { + $InputObject.AttributeChanges.Add([Microsoft.MetadirectoryServices.AttributeChange]::CreateAttributeDelete($Name)) + } + 'Replace' + { + $InputObject.AttributeChanges.Add([Microsoft.MetadirectoryServices.AttributeChange]::CreateAttributeReplace($Name,$Value)) + } + 'Rename' + { + $InputObject.AttributeChanges.Add([Microsoft.MetadirectoryServices.AttributeChange]::CreateNewDN($Value)) + } + default + { + throw "Modification type $ModificationType is not supported" + } + } + } +} +#endregion + +#region Export Helpers +function Get-CSEntryChangeValue +{ + <# + .Synopsis + Gets the value of the specified attribute of the CSEntryChange object. + .Description + Gets the value of the specified attribute of the CSEntryChange object. + .Example + Get-CSEntryChangeValue -CSEntryChange $csentryChange -AttributeName "RegistrarPool" + .Example + Get-CSEntryChangeValue -CSEntryChange $csentryChange -AttributeName "RegistrarPool" -DefaultValue "pool01.contoso.com" + .Example + Get-CSEntryChangeValue -CSEntryChange $csentryChange -AttributeName "RegistrarPool" -DefaultValue "pool01.contoso.com" -OldValue + #> + + [CmdletBinding()] + [OutputType([object])] + param( + [parameter(Mandatory = $true)] + [Microsoft.MetadirectoryServices.CSEntryChange] + $CSEntryChange, + [parameter(Mandatory = $true)] + [string] + $AttributeName, + [parameter(Mandatory = $false)] + [object] + $DefaultValue = $null, + [parameter(Mandatory = $false)] + [switch] + $OldValue + ) + + process + { + if ($CSEntryChange.AttributeChanges.Contains($AttributeName)) + { + $returnDefault = $true + + $attributeChange = $CSEntryChange.AttributeChanges[$AttributeName] + + foreach ($valueChange in $attributeChange.ValueChanges) + { + if ($OldValue) + { + if ($valueChange.ModificationType -eq "Delete") + { + $valueChange.Value # add to return pipeline + $returnDefault = $false + } + } + else + { + if ($valueChange.ModificationType -eq "Add") + { + $valueChange.Value # add to return pipeline + $returnDefault = $false + } + } + } + + if ($returnDefault) + { + $DefaultValue # return + } + } + else + { + $DefaultValue # return + } + } +} +#endregion + +function New-GenericObject +{ + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [string] + $TypeName, + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [string[]] + $TypeParameters, + [Parameter(Mandatory = $false)] + [object[]] + $ConstructorParameters + ) + + $genericTypeName = $typeName + ' + `r`n' + $typeParameters.Count + $genericType = [type]$genericTypeName + + if (!$genericType) + { + throw "Could not find generic type $genericTypeName" + } + + ## Bind the type arguments to it + $typedParameters = [Type[]]$typeParameters + $closedType = $genericType.MakeGenericType($typedParameters) + + if (!$closedType) + { + throw "Could not make closed type $genericType" + } + + ## Create the closed version of the generic type. Don't forget comma prefix + ,[Activator]::CreateInstance($closedType,$constructorParameters) +} + +Export-ModuleMember -Function * -Verbose:$false -Debug:$false + + + Validation Script + Text + Connectivity + 1 + false + + + + Schema Script + Text + Connectivity + 1 + false + [CmdletBinding()] +param( + [Parameter(Mandatory = $true)] + [System.Collections.ObjectModel.KeyedCollection[string,Microsoft.MetadirectoryServices.ConfigParameter]] + $ConfigParameters, + [Parameter(Mandatory = $false)] + [Alias('PSCredential')] # To fix mess-up of the parameter name in the RTM version of the PowerShell connector. + [System.Management.Automation.PSCredential] + $Credential, + [Parameter(Mandatory = $false)] + [ValidateScript({ Test-Path $_ -PathType "Container" })] + [string] + $ScriptDir = (Join-Path -Path $env:windir -ChildPath "TEMP") # Optional parameter for manipulation by the TestHarness script. +) + +Set-StrictMode -Version "2.0" + +$Global:DebugPreference = "Continue" +$Global:VerbosePreference = "Continue" + +$commonModule = (Join-Path -Path ([System.Environment]::GetEnvironmentVariable('Temp', 'Machine')) -ChildPath $ConfigParameters["Common Module Script Name (with extension)"].Value) + +if (!(Get-Module -Name (Get-Item $commonModule).BaseName)) { Import-Module -Name $commonModule } + +Enter-Script -ScriptType "Schema" -ErrorObject $Error + +#region function +function Get-ConnectorSchema +{ +<# + .Synopsis + Gets the connector space schema. + .Description + Gets the connector space schema defined in the "Schema.xml" file. +#> + + [CmdletBinding()] + [OutputType([Microsoft.MetadirectoryServices.Schema])] + param( + ) + + $extensionsDir = Get-ExtensionsDirectory + $schemaXml = Join-Path -Path $extensionsDir -ChildPath "Schema.xml" + + $schema = ConvertFrom-SchemaXml -SchemaXml $schemaXml + + return $schema +} + +function Write-DebugLog { + param + ( + [string]$Message + ) + + $logPath = "C:\SchemaScript.log" + $logMessage = "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") - $Message" + Add-Content -Path $logPath -Value $logMessage + +} + +$SchemaJSON = Get-ConnectorSchema | ConvertTo-Json -Depth 10 +Write-DebugLog -Message "Schema: $SchemaJSON" + +#endregion + + +Get-ConnectorSchema + +Exit-Script -ScriptType "Schema" -ErrorObject $Error + + + Additional Config Parameter Names + String + Connectivity + 1 + false + + + + Additional Encrypted Config Parameter Names + String + Connectivity + 1 + false + + + + Partition Script + Text + Global + 1 + false + + + + Hierarchy Script + Text + Global + 1 + false + + + + Begin Import Script + Text + Global + 1 + false + + + + Import Script + Text + Global + 1 + false + param( + [System.Collections.ObjectModel.KeyedCollection[string, Microsoft.MetadirectoryServices.ConfigParameter]] + [ValidateNotNull()] + $ConfigParameters, + [Microsoft.MetadirectoryServices.Schema] + [ValidateNotNull()] + $Schema, + [Microsoft.MetadirectoryServices.OpenImportConnectionRunStep] + $OpenImportConnectionRunStep, + [Microsoft.MetadirectoryServices.ImportRunStep] + $GetImportEntriesRunStep, + [pscredential] + $PSCredential +) + +#region Functions + +function Write-DebugLog { + param + ( + [string]$Message + ) + + $logPath = "C:\ImportScript.log" + $logMessage = "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") - $Message" + Add-Content -Path $logPath -Value $logMessage + +} + +function GetTokenFromCredentials { + param ( + [System.Management.Automation.PSCredential]$Credentials + ) + Write-DebugLog "Retrieving TOKEN" + $token = $Credentials.GetNetworkCredential().Password + Write-DebugLog "TOKEN Retrieval completed" + return $token + } + +#endregion + +#region SCIM Functions + +function Import-OktaProvisionedUsers { + param ( + + ) + $Token = GetTokenFromCredentials -Credentials $PSCredential + + $headers = @{ + "Accept" = "application/json" + "Content-Type" = "application/json" + "Authorization" = "SSWS $Token" + "Cookie" = "JSESSIONID=74D0E3DCA57D73E89D7019244B2255D1" + } + $results = New-Object System.Collections.Generic.List[PSObject] + + $response = Invoke-RestMethod 'https://trial-8839557-admin.okta.com//api/v1/users?filter=status eq "PROVISIONED"' -Method 'GET' -Headers $headers + foreach ($user in $response) { + $results.Add([PSCustomObject]@{ + "status" = if ($user.status -eq "PROVISIONED") { "Active" } else { "Inactive" } + "firstName" = $user.Profile.firstName + "lastName" = $user.Profile.lastName + "nickName" = $user.Profile.nickName + "displayName" = $user.Profile.displayName + "email" = $user.Profile.email + "secondEmail" = $user.Profile.secondEmail + "profileUrl" = $user.Profile.profileUrl + "preferredLanguage" = $user.Profile.preferredLanguage + "userType" = $user.Profile.userType + "organization" = $user.Profile.organization + "title" = $user.Profile.title + "division" = $user.Profile.division + "department" = $user.Profile.department + "costCenter" = $user.Profile.costCenter + "employeeNumber" = $user.Profile.employeeNumber + "mobilePhone" = $user.Profile.mobilePhone + "primaryPhone" = $user.Profile.primaryPhone + "streetAddress" = $user.Profile.streetAddress + "city" = $user.Profile.city + "state" = $user.Profile.state + "zipCode" = $user.Profile.zipCode + "countryCode" = $user.Profile.countryCode + "login" = $user.Profile.login + + }) + } + $responseDeprovisioned = Invoke-RestMethod 'https://trial-8839557-admin.okta.com//api/v1/users?filter=status eq "DEPROVISIONED"' -Method 'GET' -Headers $headers + foreach ($user in $responseDeprovisioned) { + $results.Add([PSCustomObject]@{ + "status" = if ($user.status -eq "DEPROVISIONED") { "Inactive" } else { "Active" } + "firstName" = $user.Profile.firstName + "lastName" = $user.Profile.lastName + "nickName" = $user.Profile.nickName + "displayName" = $user.Profile.displayName + "email" = $user.Profile.email + "secondEmail" = $user.Profile.secondEmail + "profileUrl" = $user.Profile.profileUrl + "preferredLanguage" = $user.Profile.preferredLanguage + "userType" = $user.Profile.userType + "organization" = $user.Profile.organization + "title" = $user.Profile.title + "division" = $user.Profile.division + "department" = $user.Profile.department + "costCenter" = $user.Profile.costCenter + "employeeNumber" = $user.Profile.employeeNumber + "mobilePhone" = $user.Profile.mobilePhone + "primaryPhone" = $user.Profile.primaryPhone + "streetAddress" = $user.Profile.streetAddress + "city" = $user.Profile.city + "state" = $user.Profile.state + "zipCode" = $user.Profile.zipCode + "countryCode" = $user.Profile.countryCode + + }) + } + + Write-DebugLog "Imported Users from Okta" + $responseJSON = $results | ConvertTo-Json + Write-DebugLog "$responseJSON" + $results + + +} + + +#endregion + +Set-PSDebug -Strict +Write-DebugLog "Starting Import Script" + +$commonModule = (Join-Path -Path ([Microsoft.MetadirectoryServices.MAUtils]::MAFolder) -ChildPath $ConfigParameters['Common Module Script Name (with extension)'].Value) +Import-Module -Name $commonModule -Verbose:$false -ErrorAction Stop + +$importResults = New-Object -TypeName 'Microsoft.MetadirectoryServices.GetImportEntriesResults' + +$csEntries = New-Object -TypeName 'System.Collections.Generic.List[Microsoft.MetadirectoryServices.CSEntryChange]' + +$columnsToImport = $Schema.Types[0].Attributes + +Write-DebugLog "Loaded $($columnsToImport.Count) attributes to import" +foreach ($column in $columnsToImport) +{ + Write-DebugLog "Attribute: $($column.Name)" +} + +$recordsToImport = Import-OktaProvisionedUsers + +Write-DebugLog "Imported $($recordsToImport.Count) records" + + +foreach ($record in $recordsToImport) +{ + + Write-DebugLog 'Starting new record' + + ##TODO: Handle a missing anchor (what exception to throw?) + + $foundValidColumns = $false + + $entrySchema = $Schema.Types[0]; + $csEntry = New-xADSyncPSConnectorCSEntryChange -ObjectType $entrySchema.Name -ModificationType Add + + foreach ($column in $columnsToImport) + { + + $columnName = $column.Name + + Write-DebugLog "Processing column $columnName" + + if ($record.$columnName) + { + + Write-DebugLog 'Found column' + + $foundValidColumns = $true + + ##TODO: Support multivalue? + + $anchorAttrName = $entrySchema.AnchorAttributes[0].Name + $value = [string]$record.$columnName + + Write-DebugLog "$columnName with value equal $value" + + + if ($columnName -eq $anchorAttrName) { + + + $csEntry.AnchorAttributes.Add([Microsoft.MetadirectoryServices.AnchorAttribute]::Create($columnName, $value)) + } + + + $csEntry | Add-xADSyncPSConnectorCSAttribute -ModificationType Add -Name $columnName -Value ([Collections.IList]($record.$columnName.Split(";"))) + + } + + } + + if ($foundValidColumns) + { + + Write-DebugLog 'Publishing CSEntryChange' + + $csEntries.Add($csEntry) + + } + + Write-DebugLog 'Record completed' + +} + +##TODO: Support paging + +$importResults.CSEntries = $csEntries + +$importResults.MoreToImport = $false + +Write-Output $importResults + + + End Import Script + Text + Global + 1 + false + + + + Begin Export Script + Text + Global + 1 + false + + + + Export Script + Text + Global + 1 + false + param( + [System.Collections.ObjectModel.KeyedCollection[string, Microsoft.MetadirectoryServices.ConfigParameter]] + $ConfigParameters, + [Microsoft.MetadirectoryServices.Schema] + $Schema, + [Microsoft.MetadirectoryServices.OpenExportConnectionRunStep] + $OpenExportConnectionRunStep, + [System.Collections.Generic.IList[Microsoft.MetaDirectoryServices.CSEntryChange]] + $CSEntries, + [pscredential] + $PSCredential +) + +Set-PSDebug -Strict + + +$commonModule = (Join-Path -Path ([Microsoft.MetadirectoryServices.MAUtils]::MAFolder) -ChildPath $ConfigParameters['Common Module Script Name (with extension)'].Value) +Import-Module -Name $commonModule -Verbose:$false -ErrorAction Stop + +#region function + +function CreateCustomPSObject { + + param + ( + $PropertyNames = @() + ) + + $template = New-Object -TypeName System.Object + + foreach ($property in $PropertyNames) { + + $template | Add-Member -MemberType NoteProperty -Name $property -Value $null + + } + + return $template +} + + +function Write-DebugLog { + param + ( + [string]$Message + ) + + $logPath = "C:\ExportScript.log" + $logMessage = "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") - $Message" + Add-Content -Path $logPath -Value $logMessage + +} + +function GetTokenFromCredentials { + param ( + [System.Management.Automation.PSCredential]$Credentials + ) + Write-DebugLog "Retrieving TOKEN" + $token = $Credentials.GetNetworkCredential().Password + Write-DebugLog "TOKEN Retrieval completed" + return $token +} + + + +#region OKTA Functions +function Add-OktaProvisionedUser { + param ( + $baseObject + ) + + $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" + $Token = GetTokenFromCredentials -Credentials $PSCredential + $headers.Add("Accept", "application/json") + $headers.Add("Content-Type", "application/json") + $headers.Add("Authorization", "SSWS $Token") + $headers.Add("Cookie", "JSESSIONID=C0B3B05DB3903768410F4B0AD4EC7BE3") + + $body = @" + { + `"profile`": { + `"login`": `"Vasco.brock@example.com`", + `"firstName`": `"Vasco`", + `"lastName`": `"Brock`", + `"nickName`": `"Vasco`", + `"displayName`": `"Vasco Brock`", + `"email`": `"Vasco.brock@example.com`", + `"secondEmail`": `"Vasco@example.org`", + `"profileUrl`": `"http://www.example.com/profile`", + `"userType`": `"Employee`", + `"organization`": `"Okta`", + `"title`": `"Director`", + `"division`": `"R&D`", + `"department`": `"Engineering`", + `"costCenter`": `"10`", + `"employeeNumber`": `"187`", + `"mobilePhone`": `"+1-555-415-1337`", + `"primaryPhone`": `"+1-555-514-1337`", + `"streetAddress`": `"301 Brannan St.`", + `"city`": `"San Francisco`", + `"state`": `"CA`", + `"zipCode`": `"94107`", + `"countryCode`": `"US`" + } + } +"@ + $body = $body.Replace("Vasco.brock@example.com", $($baseObject.login)) + $body = $body.Replace("Vasco", $($baseObject.firstName)) + $body = $body.Replace("Brock", $($baseObject.lastName)) + $body = $body.Replace("Vasco", $($baseObject.nickName)) + $body = $body.Replace("Vasco Brock", $($baseObject.displayName)) + $body = $body.Replace("Vasco.brock@example.com", $($baseObject.email)) + $body = $body.Replace("Vasco@example.org", $($baseObject.secondEmail)) + $body = $body.Replace("http://www.example.com/profile", $($baseObject.profileUrl)) + #$body = $body.Replace("en-US", $($baseObject.preferredLanguage)) + $body = $body.Replace("Employee", $($baseObject.userType)) + $body = $body.Replace("Okta", $($baseObject.organization)) + $body = $body.Replace("Director", $($baseObject.title)) + $body = $body.Replace("R&D", $($baseObject.division)) + $body = $body.Replace("Engineering", $($baseObject.department)) + $body = $body.Replace("10", $($baseObject.costCenter)) + $body = $body.Replace("187", $($baseObject.employeeNumber)) + $body = $body.Replace("+1-555-415-1337", $($baseObject.mobilePhone)) + $body = $body.Replace("+1-555-514-1337", $($baseObject.primaryPhone)) + $body = $body.Replace("301 Brannan St.", $($baseObject.streetAddress)) + $body = $body.Replace("San Francisco", $($baseObject.city)) + $body = $body.Replace("CA", $($baseObject.state)) + $body = $body.Replace("94107", $($baseObject.zipCode)) + $body = $body.Replace("US", $($baseObject.countryCode)) + Write-DebugLog -Message "Body for AddObject:\n $body" + + try { + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + $response = Invoke-RestMethod 'https://trial-8839557-admin.okta.com//api/v1/users?activate=true' -Method 'POST' -Headers $headers -Body $body + $response = $response | ConvertTo-Json + Write-DebugLog -Message "Response: $response" + + + } + catch { + Write-DebugLog -Message "Error: $_" + } + + +} + +function Update-OktaUser { + param ( + + [Parameter(Mandatory = $true)] + [string]$attributeName, + + [Parameter(Mandatory = $true)] + [string]$attributeValue, + + [Parameter(Mandatory = $true)] + [string]$userId + ) + + $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" + $token = GetTokenFromCredentials -Credentials $PSCredential + $headers.Add("Accept", "application/json") + $headers.Add("Content-Type", "application/json") + $headers.Add("Authorization", "SSWS $token") + $headers.Add("Cookie", "JSESSIONID=D6E791D528C893BC8B32EDDA0FD2A8F7") + + if ($attributeName -eq "status") { + if ($attributeValue -eq "Active") { + try { + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + $response = Invoke-RestMethod "https://trial-8839557-admin.okta.com//api/v1/users/$userId/lifecycle/activate?sendEmail=false" -Method 'POST' -Headers $headers + $response = $response | ConvertTo-Json + Write-DebugLog -Message "Response: $response" + + } + catch { + Write-DebugLog -Message "Error: $_" + } + + } + if ($attributeValue -eq "Inactive") { + try { + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + $response = Invoke-RestMethod "https://trial-8839557-admin.okta.com//api/v1/users/$userId/lifecycle//deactivate?sendEmail=false" -Method 'POST' -Headers $headers + $response = $response | ConvertTo-Json + Write-DebugLog -Message "Response: $response" + + } + catch { + Write-DebugLog -Message "Error: $_" + } + } + } + else { + $body = @{ + profile = @{ + $attributeName = $attributeValue + } + } | ConvertTo-Json + try { + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + $response = Invoke-RestMethod "https://trial-8839557-admin.okta.com//api/v1/users/$userId" -Method 'POST' -Headers $headers -Body $body + $response = $response | ConvertTo-Json + Write-DebugLog -Message "Response: $response" + + + } + catch { + Write-DebugLog -Message "Error: $_" + } + + } + + + +} + +function Delete-OktaUser { + param ( + + [Parameter(Mandatory = $true)] + [string]$userId + + ) + + $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" + $token = GetTokenFromCredentials -Credentials $PSCredential + $headers.Add("Accept", "application/json") + $headers.Add("Content-Type", "application/json") + $headers.Add("Authorization", "SSWS $token") + $headers.Add("Cookie", "JSESSIONID=D6E791D528C893BC8B32EDDA0FD2A8F7") + + + + try { + + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + $url = "https://trial-8839557-admin.okta.com//api/v1/users/$userId" + Write-DebugLog -Message "URL: $url" + $response = Invoke-RestMethod -Uri $url -Method 'DELETE' -Headers $headers + $response = $response | ConvertTo-Json + Write-DebugLog -Message "Response: $response" + + + } + catch { + Write-DebugLog -Message "Error: $_" + } + + +} + +#endregion + +Write-DebugLog -Message "-----------------------------------------------------------------------------" +Write-DebugLog -Message "Starting Export Script Main Function" + +$csentryChangeResults = New-Object "System.Collections.Generic.List[Microsoft.MetadirectoryServices.CSEntryChangeResult]" + + +$columnsToExport = @() + +foreach ($attribute in $Schema.Types[0].Attributes) { + + $columnsToExport += $attribute.Name + + Write-DebugLog -Message "Added attribute $($attribute.Name) to export list" + +} + + + +Write-DebugLog -Message "Processing object $($entry.Identifier)" + + +foreach ($entry in $CSEntries) { + + + + Write-DebugLog -Message "-------------------------------------------------------------------------------------------------------" + + foreach ($attributeName in $entry.ChangedAttributeNames) { + Write-DebugLog -Message "Attribute: $attributeName" + Write-DebugLog -Message "Value: $($entry.AttributeChanges[$attributeName].ValueChanges[0].Value)" + $anchorAttributeValue = $entry.AnchorAttributes[0].Value.ToString(); + Write-DebugLog -Message "Anchor Attribute: $($entry.AnchorAttributes[0].Name)" + Write-DebugLog -Message "Anchor Value: $anchorAttributeValue" + if ($entry.ObjectModificationType -eq 'Replace') { + Update-OktaUser -attributeName $attributeName -attributeValue $entry.AttributeChanges[$attributeName].ValueChanges[0].Value -userId $anchorAttributeValue + + + } + + + } + + Write-DebugLog -Message "Processing object $($entry.Identifier). ObjectModificationType $($entry.ObjectModificationType)" + + [bool]$objectHasAttributes = $false + + + $baseObject = CreateCustomPSObject -PropertyNames $columnsToExport + + if ($entry.ObjectModificationType -eq 'Replace') { + $anchorAttributeName = $entry.AnchorAttributes[0].Name; + $anchorAttributeValue = $entry.AnchorAttributes[0].Value.ToString(); + + + + foreach ($attribute in $entry.ChangedAttributeNames) { + + $value = Get-CSEntryChangeValue -CSEntryChange $entry -AttributeName $attribute + Write-DebugLog -Message " Changed Attribute Value is : $value" + + + + } + } + + + if ($entry.ObjectModificationType -ne 'Delete') { + + foreach ($attribute in $columnsToExport) { + + if (($entry.AttributeChanges.Contains($attribute)) -eq $false -and ($entry.AnchorAttributes.Contains($attribute) -eq $false)) { + + continue + + } + + + if ($entry.AnchorAttributes[$attribute].Value) { + + $baseObject.$attribute = $entry.AnchorAttributes[$attribute].Value + + $objectHasAttributes = $true + + } + + elseif ($entry.AttributeChanges[$attribute].ValueChanges[0].Value) { + + $baseObject.$attribute = ($entry.AttributeChanges[$attribute].ValueChanges | Select-Object -Expand Value) -join ";" + + $objectHasAttributes = $true + + } + elseif ($entry.AttributeChanges[$attribute].DataType -eq "Boolean") { + $baseObject.$attribute = ($entry.AttributeChanges[$attribute].ValueChanges | Select-Object -Expand Value) -join ";" + + + + } + + } + + if ($objectHasAttributes) { + + foreach ($property in $baseObject.PSObject.Properties) { + if ($property.Value -eq $null) { + $baseObject.($property.Name) = "" + } + } + + + foreach ($property in $baseObject.PSObject.Properties) { + Write-DebugLog -Message "Added $($property.Name) with value $($property.Value)" + } + if ($entry.ObjectModificationType -eq 'Add') { + Write-DebugLog -Message "Adding user to OKTA with Email $($baseObject.Email) and DisplayName $($baseObject.DisplayName) and UserName $($baseObject.UserName) and AzureObjectID $($baseObject.AzureObjectID)" + Add-OktaProvisionedUser -baseObject $baseObject + } + + + + } + + } + else { + $anchorAttributeName = $entry.AnchorAttributes[0].Name; + $anchorAttributeValue = $entry.AnchorAttributes[0].Value.ToString(); + Write-DebugLog -Message "Delete the object with attribute '$($anchorAttributeName)' equals '$($anchorAttributeValue)'" + Delete-OktaUser -userId $anchorAttributeValue + } + + $csentryChangeResult = [Microsoft.MetadirectoryServices.CSEntryChangeResult]::Create($entry.Identifier, $null, "Success") + $csentryChangeResults.Add($csentryChangeResult) + + Write-DebugLog -Message "Completed processing object $($entry.Identifier)" + +} + +$closedType = [type]"Microsoft.MetadirectoryServices.PutExportEntriesResults" + +return [Activator]::CreateInstance($closedType, $csentryChangeResults) + + + End Export Script + Text + Global + 1 + false + + + + Begin Password Script + Text + Global + 1 + false + + + + Password Extension Script + Text + Global + 1 + false + + + + End Password Script + Text + Global + 1 + false + + + + PowerShell script execution timeout in minutes (0 – disabled) + String + Global + 1 + false + 0 + + + PageSizeFullImport + Text + RunStep + 0 + false + 500 + + + + + a0baba40-8bca-4e58-9d4b-38851f12af74 + Export + 0 + 2024-03-01T05:36:04.7168519-08:00 + 2024-03-01T05:36:04.7168519-08:00 + + + Export + None + default + 0 + 0 + + + PageSizeExport + Text + RunStep + 0 + false + 500 + + + + + + + 084d8a3a-fd7d-4092-8ed8-0522c5d2d6b7 + FullImport + 0 + 2024-03-01T05:36:09.2460519-08:00 + 2024-03-01T05:36:09.2460519-08:00 + + + Import + None + default + 0 + 0 + + + PageSizeFullImport + Text + RunStep + 0 + false + 500 + + + + + + + true + true + false + false + + + + + User + Person + + + login + true + false + false + false + false + String + + + -dn- + false + true + false + false + false + String + + + city + false + false + false + false + false + String + + + costCenter + false + false + false + false + false + String + + + countryCode + false + false + false + false + false + String + + + department + false + false + false + false + false + String + + + displayName + false + false + false + false + false + String + + + division + false + false + false + false + false + String + + + email + false + false + false + false + false + String + + + employeeNumber + false + false + false + false + false + String + + + firstName + false + false + false + false + false + String + + + lastName + false + false + false + false + false + String + + + mobilePhone + false + false + false + false + false + String + + + nickName + false + false + false + false + false + String + + + organization + false + false + false + false + false + String + + + preferredLanguage + false + false + false + false + false + String + + + primaryPhone + false + false + false + false + false + String + + + profileUrl + false + false + false + false + false + String + + + secondEmail + false + false + false + false + false + String + + + state + false + false + false + false + false + String + + + streetAddress + false + false + false + false + false + String + + + title + false + false + false + false + false + String + + + userType + false + false + false + false + false + String + + + zipCode + false + false + false + false + false + String + + + status + false + false + false + false + true + String + + + false + + + + TreatAsHard + None + + + + + + 0a703075-55f6-448a-a3b4-892f8f81f5fa + default + default + + + False + False + + + + Microsoft.IAM.Connector.PowerShell + 120 + + PowershellRestAPI + Powershell Connector For Connecting With Rest API Application. + Friday, March 1, 2024 4:58:41 AM + Friday, March 1, 2024 4:58:41 AM + true + + + + + 8585 + + + \ No newline at end of file