Build-FreeIPAModule.psm1

#
# Created by: lucas.cueff[at]lucas-cueff.com
# Build by : Lucas Cueff
# v0.7 :
# - First Release
#
# Released on: 18/11/2018
#
#'(c) 2018 lucas-cueff.com - Distributed under Artistic Licence 2.0 (https://opensource.org/licenses/artistic-license-2.0).'

<#
    .SYNOPSIS
    Powershell cmdlets to build FreeIPA Poweshell Module management
 
    .DESCRIPTION
    Build-iPAModule.psm1 module provides a commandline interface to build your own FreeIPA/IPA Management Powershell Module. Works for Powershell (Core/Classic(starting v4) - Windows/Linux/Mac Os).
    This module will use JSON_Metadata and env APIs from your IPA Server to dynamically generate all functions code and binding for your personal environment.
    For more information on the FreeIPA API, please connect to thw web interface on your IPA Server : https://yourIPA.tld/ipa/ui/#/p/apibrowser/type=command
    Note : Don't forget to trust your IPA AC / ssl certificate locally before using the Powershell Module.
     
    .EXAMPLE
    C:\PS> import-module Build-FreeIPAModule.psm1
#>

Function Get-ScriptDirectory {
    Split-Path -Parent $PSCommandPath
}
Function Export-IPASchema {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$false,ValueFromPipelineByPropertyName=$true)]
        [ValidateNotNullOrEmpty()]
            [string]$IPASchemaRef
    )
    If (!($IPASchemaRef)) {
        $IPASchemaRef = join-path (Get-ScriptDirectory) "IPAschema.xml"
    }
    export-clixml -path $IPASchemaRef -InputObject (Invoke-FreeIPAAPIJson_Metadata).commands
}
Function Export-IPAEnv {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$false,ValueFromPipelineByPropertyName=$true)]
        [ValidateNotNullOrEmpty()]
            [string]$IPAEnvRef
    )
    If (!($IPAEnvRef)) {
        $IPAEnvRef = join-path (Get-ScriptDirectory) "IPAenv.xml"
    }
    export-clixml -path $IPAEnvRef -InputObject (Invoke-FreeIPAAPIEnv)
}
Function Invoke-FreeIPAAPIEnv {
    [CmdletBinding()]
    [OutputType([psobject])]
    param (
        [parameter(Mandatory=$false)]
            [switch]$All,
        [parameter(Mandatory=$false)]
            [switch]$FullResultsOutput
    )
    process {
        $EnvParams = New-Object psobject -property @{
            version = $global:FreeIPAAPIServerConfig.ClientVersion
        }
        if ($All.IsPresent) {
            $EnvParams | Add-Member -NotePropertyName all -NotePropertyValue $true
        }
        $EnvObject = New-Object psobject -property @{
            id        = 0
            method    = "env/1"
            params  = @(@(),$EnvParams)
        }
        if (!($FullResultsOutput.IsPresent)) {
            (Invoke-FreeIPAAPI $EnvObject).result.result
        } else {
            Invoke-FreeIPAAPI $EnvObject
        }
    } 
}
Function Get-IPAPCmdletName {
    [CmdletBinding()]
    [OutputType([psobject])]
    param (
        [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)]
        [ValidateNotNullOrEmpty()]
        [Object]$inputSchemaObject
    )
    process {
        if (!($inputSchemaObject.name) -and !($inputSchemaObject.doc)) {
            throw "Input object not valid"
        }
        $IPAAPIName = $inputSchemaObject.name
        $IPAAPIDoc = $inputSchemaObject.doc
        switch (($IPAAPIName -split '_').count) {
            1 {
                New-Object psobject -property @{
                    PWshFunctionName = "Invoke-FreeIPAAPI" + (Get-Culture).TextInfo.ToTitleCase($IPAAPIName)
                    PWshAliasName     = "Use-IPA" + (Get-Culture).TextInfo.ToTitleCase($IPAAPIName)
                    CmdletDoc        = $IPAAPIDoc
                }
            }
            2 {
                $TmpAPIName = ($IPAAPIName -split '_')[0]
                $TmpPWSVerb = ($IPAAPIName -split '_')[1]
                switch ($TmpPWSVerb) {
                    "mod" {$PWshAliasName = "Set-IPA" + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[0])}
                    "undel" {$PWshAliasName = "Restore-IPA" + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[0])}
                    "status" {$PWshAliasName = "Get-IPAStatus" + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[0])}
                    "reinitialize" {$PWshAliasName = "Initialize-IPA" + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[0])}
                    "activate" {$PWshAliasName = "Enable-IPA" + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[0])}
                    "logout" {$PWshAliasName = "Disconnect-IPA"}
                    "unapply" {$PWshAliasName = "Unregister-IPA" + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[0])}
                    "metadata" {$PWshAliasName = "Get-IPAMetadata"}
                    "conncheck" {$PWshAliasName = "Test-IPAConnection"}
                    "verify" {$PWshAliasName = "Confirm-IPA" + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[0])}
                    "delentry" {$PWshAliasName = "Remove-IPA" + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[0]) + "Entry"}
                    "rebuild" {$PWshAliasName = "Build-IPA" + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[0])}
                    "apply" {$PWshAliasName = "Publish-IPA" + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[0])}
                    "unapply" {$PWshAliasName = "Undo-IPA" + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[0])}
                    "del" {$PWshAliasName = "Remove-IPA" + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[0])}
                    "defaults" {$PWshAliasName = "Get-IPADefaults" + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[1])}
                    "tofiles" {$PWshAliasName = "Set-IPATofiles" + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[0])}
                    "ds" {$PWshAliasName = "Do-IPADs" + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[0])}
                    "messages" {$PWshAliasName = "Get-IPAMessages" + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[0])}
                    "match" {$PWshAliasName = "Search-IPAMatch" + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[0])}
                    "detach" {$PWshAliasName = "Remove-IPAManaged" + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[0])}
                    "stage" {$PWshAliasName = "Move-IPADelToStage" + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[0])}
                    "tofiles" {$PWshAliasName = "Build-IPAFiles" + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[0])}
                    Default {$PWshAliasName = (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[1]) + "-IPA" + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[0])}
                }
                New-Object psobject -property @{
                    PWshFunctionName = "Invoke-FreeIPAAPI" + (Get-Culture).TextInfo.ToTitleCase($IPAAPIName)
                    PWshAliasName     = $PWshAliasName
                    CmdletDoc        = $IPAAPIDoc
                }
            }
            3 {
                $TmpAPIName = ($IPAAPIName -split '_')[0]
                $TmpPWSVerb = ($IPAAPIName -split '_')[1]
                switch ($TmpPWSVerb) {
                    "archive" {$PWshAliasName = "Move-IPAToArchive" + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[0]) + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[2])}
                    "fetch" {$PWshAliasName = "Build-IPA" + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[0]) + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[2])}
                    "is" {$PWshAliasName = "Test-IPA" + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[0]) + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[2])}
                    "was" {$PWshAliasName = "Get-IPAStatus" + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[0]) + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[2])}
                    "mod" {$PWshAliasName = "Set-IPA" + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[0]) + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[2])}
                    "retrieve" {$PWshAliasName = "Get-IPA" + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[0]) + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[2])}
                    "role" {$PWshAliasName = (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[2]) + "-IPA" + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[0]) + "Role"}
                    Default {$PWshAliasName = (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[1]) + "-IPA" + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[0]) + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[2])}
                }
                New-Object psobject -property @{
                    PWshFunctionName = "Invoke-FreeIPAAPI" + (Get-Culture).TextInfo.ToTitleCase($IPAAPIName)
                    PWshAliasName     = $PWshAliasName
                    CmdletDoc        = $IPAAPIDoc
                }
            }
            4 {
                $TmpAPIName = ($IPAAPIName -split '_')[0]
                $TmpPWSVerb = ($IPAAPIName -split '_')[1]
                switch ($TmpPWSVerb) {
                    "disallow" {$PWshAliasName = "Deny-IPA" + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[0]) + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[2]) + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[3])}
                    "allow" {$PWshAliasName = "Approve-IPA" + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[0]) + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[2]) + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[3])}
                    "default" {$PWshAliasName = ($IPAAPIName -split '_')[3] + "-IPA" + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[0]) + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[1]) + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[2])}
                    Default {$PWshAliasName = (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[1]) + "-IPA" + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[0]) + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[2]) + (Get-Culture).TextInfo.ToTitleCase(($IPAAPIName -split '_')[3])}
                }
                New-Object psobject -property @{
                    PWshFunctionName = "Invoke-FreeIPAAPI" + (Get-Culture).TextInfo.ToTitleCase($IPAAPIName)
                    PWshAliasName     = $PWshAliasName
                    CmdletDoc        = $IPAAPIDoc
                }
            }      
        }
    }
}
Function Get-IPACmdletBinding {
    [CmdletBinding()]
    [OutputType([psobject])]
    param (
        [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)]
        [ValidateNotNullOrEmpty()]
            [Object]$inputSchemaObject,
        [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$false)]
        [ValidateSet("BindingValue", "All", "BindingCondition")]
            [string]$InfoType
    ) 
    process {
        if (!($inputSchemaObject.takes_options) -and !($inputSchemaObject.takes_args)) {
            throw "Input object not valid"
        }
        $Objargs = $inputSchemaObject.takes_args
        $Objoptions = $inputSchemaObject.takes_options
        foreach ($arg in $Objoptions) {
            switch ($InfoType) {
                "BindingValue" { Get-IPACmdletBindingValue $arg }
                "BindingCondition" { Get-IPABindingCondition $arg }
                Default {
                    Get-IPACmdletBindingValue $arg
                    Get-IPABindingCondition $arg
                 }
            }
        }
        foreach ($arg in $Objargs) {
            If ($arg.gettype().fullname -ne "System.String") {
                switch ($InfoType) {
                    "BindingValue" { Get-IPACmdletBindingValue $arg }
                }
            }
        }
    }
}
Function Get-IPACmdletBindingValue {
    [CmdletBinding()]
    [OutputType([psobject])]
    param (
        [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)]
        [ValidateNotNullOrEmpty()]
            [Object]$inputSchemaObject
    ) 
    process {
        if (!($inputSchemaObject.cli_name) -and !($inputSchemaObject.cli_metavar)) {
            $inputSchemaObject
            throw "Input object not valid"
        }
        If (!($inputSchemaObject.deprecated) -and ($inputSchemaObject.doc -notlike "deprecated*")) {
            switch ($inputSchemaObject.cli_metavar) {
                "DATETIME" {$variablecontent = "[System.DateTime]" + "$" + $inputSchemaObject.cli_name}
                "INT" {$variablecontent = "[int]" + "$" + $inputSchemaObject.cli_name}
                "FLAG" {$variablecontent = "[switch]" + "$" + $inputSchemaObject.cli_name}
                "BOOL" {$variablecontent = "[switch]" + "$" + $inputSchemaObject.cli_name}
                "PASSWORD" {$variablecontent = "[SecureString]" + "$" + $inputSchemaObject.cli_name}
                Default {
                    if ($inputSchemaObject.cli_name -eq "password") {
                        $variablecontent = "[SecureString]" + "$" + $inputSchemaObject.cli_name
                    } elseif ($inputSchemaObject.multivalue) {
                        $variablecontent = "[String[]]" + "$" + $inputSchemaObject.cli_name
                    } else {
                        $variablecontent = "[String]" + "$" + $inputSchemaObject.cli_name
                    }
                }
            }
            if ($inputSchemaObject.pattern) {
                $validation = '[ValidatePattern(' + '"' + $inputSchemaObject.pattern + '")]'
            } elseif ($inputSchemaObject.values) {
                $validation = '[ValidateSet(' + (($inputSchemaObject.values | ForEach-Object {'"{0}"' -f $_}) -join ",") + ')]'
            } else {
                if ($inputSchemaObject.cli_metavar -ne "FLAG") {
                    $validation = "[ValidateNotNullOrEmpty()]"
                }
            }
            if (($inputSchemaObject.cli_metavar -eq "BOOL") -or ($inputSchemaObject.cli_metavar -eq "FLAG")) {
                $parameterval = "[parameter(Mandatory=" + "$" + "false)]"
            } elseif ($inputSchemaObject.required -and !($inputSchemaObject.default)) {
                $parameterval = "[parameter(Mandatory=" + "$" + "true)]"
            } else {
                $parameterval = "[parameter(Mandatory=" + "$" + "false)]"
            }
            New-Object psobject -property @{
                Parameter        = $parameterval
                Validation        = $validation
                Variableandtype = $variablecontent
                APIParam        = $inputSchemaObject.name
                Help            = $inputSchemaObject.doc
                Variable        = $inputSchemaObject.cli_name
            }
        }
    }
}
Function Get-IPABindingCondition {
    [CmdletBinding()]
    [OutputType([psobject])]
    param (
        [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)]
        [ValidateNotNullOrEmpty()]
            [Object]$inputSchemaObject
    ) 
    process {
        if (!($inputSchemaObject.cli_name) -and !($inputSchemaObject.cli_metavar)) {
            $inputSchemaObject
            throw "Input object not valid"
        }
        switch ($inputSchemaObject.cli_metavar) {
            "DATETIME" { $variablecondition = " if (`$" + $inputSchemaObject.cli_name + ") { `$ObjParams | Add-Member -NotePropertyName " + $inputSchemaObject.name + " -NotePropertyValue " + "$" + $inputSchemaObject.cli_name + " }" }
            "INT" { $variablecondition = " if (`$" + $inputSchemaObject.cli_name + ") { `$ObjParams | Add-Member -NotePropertyName " + $inputSchemaObject.name + " -NotePropertyValue " + "$" + $inputSchemaObject.cli_name + " }" }
            "FLAG" { $variablecondition = " if (`$" + $inputSchemaObject.cli_name + ".IsPresent) { `$ObjParams | Add-Member -NotePropertyName " + $inputSchemaObject.name + " -NotePropertyValue " + "`$true" + " }" }
            "BOOL" { $variablecondition = " if (`$" + $inputSchemaObject.cli_name + ".IsPresent) { `$ObjParams | Add-Member -NotePropertyName " + $inputSchemaObject.name + " -NotePropertyValue " + "`$true" + " }" }
            Default {
                if ($inputSchemaObject.cli_name -eq "version") {
                    $variablecondition = ""
                } else {
                    $variablecondition = " if (`$" + $inputSchemaObject.cli_name + ") { `$ObjParams | Add-Member -NotePropertyName " + $inputSchemaObject.name + " -NotePropertyValue " + "$" + $inputSchemaObject.cli_name + " }"
                }
            }
        }
        New-Object psobject -property @{
            OptionCondition = $variablecondition
            APIParam        = $inputSchemaObject.name
            Variable        = $inputSchemaObject.cli_name
        }
    }
}
Function Set-FreeIPAAPICredentials {
    [cmdletbinding()]
    Param (
      [parameter(Mandatory=$true)]
      [ValidateNotNullOrEmpty()]
          [SecureString]$AdminLogin,
      [parameter(Mandatory=$true)]
      [ValidateNotNullOrEmpty()]
          [SecureString]$AdminPassword,
      [parameter(Mandatory=$false)]
          [switch]$Remove,
      [parameter(Mandatory=$false)]
          [switch]$EncryptKeyInLocalFile,
      [parameter(Mandatory=$false)]
      [ValidateNotNullOrEmpty()]
          [securestring]$MasterPassword
    )
    if ($Remove.IsPresent) {
      $global:FreeIPAAPICredentials = $Null
    } Else {
        $global:FreeIPAAPICredentials = @{
            user = $AdminLogin
            password = $AdminPassword
        }
      If ($EncryptKeyInLocalFile.IsPresent) {
          If (!$MasterPassword -or !$AdminPassword) {
              Write-warning "Please provide a valid Master Password to protect the credential storage on disk and a valid credential"
              throw 'no credential or master password'
          } Else {
              $SaltBytes = New-Object byte[] 32
              $RNG = New-Object System.Security.Cryptography.RNGCryptoServiceProvider
              $RNG.GetBytes($SaltBytes)
              $Credentials = New-Object System.Management.Automation.PSCredential -ArgumentList 'user', $MasterPassword
              $Rfc2898Deriver = New-Object System.Security.Cryptography.Rfc2898DeriveBytes -ArgumentList $Credentials.GetNetworkCredential().Password, $SaltBytes
              $KeyBytes  = $Rfc2898Deriver.GetBytes(32)
              $EncryptedPass = $AdminPassword | ConvertFrom-SecureString -key $KeyBytes
              $EncryptedLogin = $AdminLogin | ConvertFrom-SecureString -key $KeyBytes
              $ObjConfigFreeIPA = @{
                  Salt = $SaltBytes
                  EncryptedAdminSecret = $EncryptedPass
                  EncryptedAdminAccount = $EncryptedLogin
              }
              $FolderName = 'Build-IPAModule'
              $ConfigName = 'Build-IPAModule.xml'
              if (!(Test-Path -Path "$($env:AppData)\$FolderName")) {
                  New-Item -ItemType directory -Path "$($env:AppData)\$FolderName" | Out-Null
              }
              if (test-path "$($env:AppData)\$FolderName\$ConfigName") {
                  Remove-item -Path "$($env:AppData)\$FolderName\$ConfigName" -Force | out-null
              }
              $ObjConfigFreeIPA | Export-Clixml "$($env:AppData)\$FolderName\$ConfigName"
          }    
      }
    }
  }
Function Import-FreeIPAAPICrendentials {
      [CmdletBinding()]
      Param(
          [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)]
          [ValidateNotNullOrEmpty()]
          [securestring]$MasterPassword
      )
      process {
        $FolderName = 'Build-IPAModule'
        $ConfigName = 'Build-IPAModule.xml'
          if (!(Test-Path "$($env:AppData)\$($FolderName)\$($ConfigName)")){
              Write-warning 'Configuration file has not been set, Set-FreeIPAAPICredentials to configure the credentials.'
              throw 'error config file not found'
          }
          $ObjConfigFreeIPA = Import-Clixml "$($env:AppData)\$($FolderName)\$($ConfigName)"
          $Credentials = New-Object System.Management.Automation.PSCredential -ArgumentList 'user', $MasterPassword
          try {
              $Rfc2898Deriver = New-Object System.Security.Cryptography.Rfc2898DeriveBytes -ArgumentList $Credentials.GetNetworkCredential().Password, $ObjConfigFreeIPA.Salt
              $KeyBytes  = $Rfc2898Deriver.GetBytes(32)
              $SecStringPass = ConvertTo-SecureString -Key $KeyBytes $ObjConfigFreeIPA.EncryptedAdminSecret
              $SecStringLogin = ConvertTo-SecureString -Key $KeyBytes $ObjConfigFreeIPA.EncryptedAdminAccount
              $global:FreeIPAAPICredentials = @{
                  user = $SecStringLogin
                  password = $SecStringPass
              }
          } catch {
              write-warning "Not able to set correctly your credential, your passphrase my be incorrect"
              write-verbose -message "Error Type: $($_.Exception.GetType().FullName)"
              write-verbose -message "Error Message: $($_.Exception.Message)"
          }
      }
  }
Function Set-FreeIPAAPIServerConfig {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)]
        [ValidateScript({$_ -match "(http[s]?)(:\/\/)([^\s,]+)"})]
            [String]$URL,
        [parameter(Mandatory=$false)]
        [ValidateNotNullOrEmpty()]
            [String]$ClientVersion
    )
    process {
        $global:FreeIPAAPIServerConfig = @{
            ServerURL = $URL
        }
        if ($ClientVersion) {
            $global:FreeIPAAPIServerConfig.add('ClientVersion',$ClientVersion)
        } else {
            $global:FreeIPAAPIServerConfig.add('ClientVersion',"2.229")
        }
    }
  }
Function Get-FreeIPAAPIAuthenticationCookie {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$false,ValueFromPipelineByPropertyName=$true)]
        [ValidateScript({$_ -match "(http[s]?)(:\/\/)([^\s,]+)"})]
            [String]$URL,
        [parameter(Mandatory=$false)]
        [ValidateNotNullOrEmpty()]
            [SecureString]$AdminLogin,
        [parameter(Mandatory=$false)]
        [ValidateNotNullOrEmpty()]
            [SecureString]$AdminPassword,
        [parameter(Mandatory=$false)]
            [switch]$UseCachedURLandCredentials,
        [parameter(Mandatory=$false)]
            [switch]$CloseAllRemoteSession
    )
    if ($CloseAllRemoteSession.IsPresent) {
        Invoke-FreeIPAAPISessionLogout
    } else {
        if (!($UseCachedURLandCredentials.IsPresent)) {
            if ($URL -and $AdminLogin -and $AdminPassword) {
                $global:FreeIPAAPICredentials = @{
                    user = $AdminLogin
                    password = $AdminPassword
                }
                Set-FreeIPAAPIServerConfig -URL $URL

            } else {
                write-warning "if UseCachedURLandCredentials switch is not used URL, AdminLogin, AdminPassword parameters must be used"
                throw 'AdminLogin, AdminPassword parameters must be used'
            }
        }
        try {
            $SecureStringPassToBSTR = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($global:FreeIPAAPICredentials.password)
            $SecureStringLoginToBSTR = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($global:FreeIPAAPICredentials.user)
            $BSTRCredentials = @{
                user = [Runtime.InteropServices.Marshal]::PtrToStringAuto($SecureStringLoginToBSTR)
                password = [Runtime.InteropServices.Marshal]::PtrToStringAuto($SecureStringPassToBSTR) 
            }
            $FreeIPALogin = Invoke-WebRequest "$($global:FreeIPAAPIServerConfig.ServerURL)/ipa/session/login_password" -Session 'FunctionFreeIPASession' -Body $BSTRCredentials -Method 'POST'
        } catch [System.Net.Http.HttpRequestException] {
            switch ($_.Exception.Response.StatusCode.value__) {
                404 { 
                        Write-error -message "Please check that /ipa/session/login_password is available on your FreeIPA server"
                    }
                Default {
                            write-error -message "HTTP Error Code $($_.Exception.Response.StatusCode.Value__) with error message:$($_.Exception.Response.StatusDescription)"
                        }
            }
            Break
        } catch [System.Exception] {
            write-error -message "other error encountered - error message:$($_.Exception.message)"
            break
        }
        $global:FreeIPASession = $FunctionFreeIPASession
        $FreeIPALogin
    }
}
Function Invoke-FreeIPAAPIJson_Metadata {
    [CmdletBinding()]
    [OutputType([psobject])]
    param (
        [Parameter(Mandatory=$false,ValueFromPipelineByPropertyName=$true)]
        [ValidateNotNullOrEmpty()]
            [String]$ObjName,
        [Parameter(Mandatory=$false)]
        [ValidateNotNullOrEmpty()]
            [String]$MethodName,
        [Parameter(Mandatory=$false)]
        [ValidateNotNullOrEmpty()]
            [String]$Object,
        [Parameter(Mandatory=$false)]
        [ValidateNotNullOrEmpty()]
            [String]$Method,
        [Parameter(Mandatory=$false)]
        [ValidateNotNullOrEmpty()]
            [String]$Command,
        [parameter(Mandatory=$false)]
            [switch]$FullResultsOutput
    )
    process {
        $JsonMetadataParams = New-Object psobject -property @{
            version = $global:FreeIPAAPIServerConfig.ClientVersion
        }
        if ($Object) {
            $JsonMetadataParams | Add-Member -NotePropertyName object -NotePropertyValue $Object
        }
        if ($Method) {
            $JsonMetadataParams | Add-Member -NotePropertyName method -NotePropertyValue $Method
        }
        if ($Command) {
            $JsonMetadataParams | Add-Member -NotePropertyName command -NotePropertyValue $Command
        }
        $JsonMetadataObject = New-Object psobject -property @{
            id        = "0"
            method    = "json_metadata/1"
            params  = @(@($ObjName,$MethodName),$JsonMetadataParams)
        }
        if (!($FullResultsOutput.IsPresent)) {
            (Invoke-FreeIPAAPI $JsonMetadataObject).result
        } else {
            Invoke-FreeIPAAPI $JsonMetadataObject
        }
    }
}
Function Invoke-FreeIPAAPISessionLogout {
    [CmdletBinding()]
    [OutputType([psobject])]
    param (
        [parameter(Mandatory=$false)]
        [switch]$FullResultsOutput
    )
    process {
        $SessionLogoutParams = New-Object psobject -property @{
            version = $global:FreeIPAAPIServerConfig.ClientVersion
        }
        $SessionLogoutObject = New-Object psobject -property @{
            id        = 0
            method    = "session_logout/1"
            params  = @(@(),$SessionLogoutParams)
        }
        if (!($FullResultsOutput.IsPresent)) {
            (Invoke-FreeIPAAPI $SessionLogoutObject).result.result
        } else {
            Invoke-FreeIPAAPI $SessionLogoutObject
        }
    }
}
Function Invoke-FreeIPAAPI {
    [CmdletBinding()]
    [OutputType([psobject])]
    param (
        [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)]
        [ValidateNotNullOrEmpty()]
            [Object]$inputAPIObject
    )
    begin {
        If (!(get-variable FreeIPASession)) {
            throw "Please use Get-FreeIPAAPIAuthenticationCookie function first to get an authentication cookie"
        }
    } process {
        try {
            $json = $inputAPIObject | ConvertTo-Json -Depth 3
            Write-Verbose -message $json
        } catch {
            write-error -message "Not able to convert input object to json"
            break
        }
        try {
            if ($json) {
                Invoke-RestMethod "$($global:FreeIPAAPIServerConfig.ServerURL)/ipa/session/json" -Method Post -WebSession $global:FreeIPASession -Body $json -ContentType 'application/json' -Headers @{"Referer"="$($global:FreeIPAAPIServerConfig.ServerURL)/ipa/session/json"}
            }            
        } catch [System.Net.Http.HttpRequestException] {
            switch ($_.Exception.Response.StatusCode.value__) {
                404 { 
                        Write-error -message "Please check that /ipa/session/json is available on your FreeIPA server"
                    }
                Default {
                            write-error -message "HTTP Error Code $($_.Exception.Response.StatusCode.Value__) with error message:$($_.Exception.Response.StatusDescription)"
                        }
            }
            Break
        } catch {
            write-error -message "error - please troubleshoot - error message:$($_.Exception.message)"
            break
        }
    }
}
Function Publish-IPAModule {
    [CmdletBinding()]
    [OutputType([psobject])]
    param (
        [Parameter(Mandatory=$false,ValueFromPipelineByPropertyName=$true)]
        [ValidateNotNullOrEmpty()]
            [string]$IPASchemaRef,
        [Parameter(Mandatory=$false)]
        [ValidateNotNullOrEmpty()]
            [string]$IPAEnvRef,
        [Parameter(Mandatory=$false)]
        [ValidateNotNullOrEmpty()]
            [string]$IPAModuleFile,
        [Parameter(Mandatory=$false)]
        [ValidateNotNullOrEmpty()]
            [string]$BuilderName,
        [Parameter(Mandatory=$false)]
        [ValidateNotNullOrEmpty()]
            [string]$Version,
        [Parameter(Mandatory=$false)]
        [ValidateNotNullOrEmpty()]
            [string]$VersionComment
    )
    $script:BuildingDate = get-date
    $buildtimestamp = $script:BuildingDate.Ticks.ToString()
    $script:PwshModuleTemplate = join-path (Get-ScriptDirectory) "Manage-FreeIPA_Module_Template.file"
    $script:PwshManifestTemplate = join-path (Get-ScriptDirectory) "Manage-FreeIPA_Mainfest_Template.file"
    If (!($IPASchemaRef)) {
        $IPASchemaRef = join-path (Get-ScriptDirectory) "IPAschema.xml"
    }
    If (!($IPAEnvRef)) {
        $IPAEnvRef = join-path (Get-ScriptDirectory) "IPAenv.xml"
    }
    If (!($IPAModuleFile)) {
        $IPAModuleFile = "Manage-FreeIPA_Build_" + $buildtimestamp + ".psm1"
        $IPAModuleFile = join-path (Get-ScriptDirectory) $IPAModuleFile
        $IPAManifestFile = "Manage-FreeIPA_Build_" + $buildtimestamp + ".psd1"
        $IPAManifestFile = join-path (Get-ScriptDirectory) $IPAManifestFile
    } else {
        $IPAManifestFile = $IPAModuleFile.replace(".psm1",".psd1")
    }
    If (!(test-path $IPASchemaRef)) {
        throw "IPA Schema file does not exist."
    }
    If (!(test-path $IPAEnvRef)) {
        throw "IPA Env file does not exist."
    }
    If (!(test-path $script:PwshModuleTemplate) -or !(test-path $script:PwshManifestTemplate)) {
        throw "IPA Powershell module templates missing."
    }
    $script:objEnv = Import-Clixml -Path $IPAEnvRef
    get-content -path $script:PwshModuleTemplate | add-content -path $IPAModuleFile | Out-Null
    get-content -path $script:PwshManifestTemplate | add-content -path $IPAManifestFile | Out-Null
    $TMPManifestContent = (get-content -path $IPAManifestFile).replace("%builddate%",($script:BuildingDate.ToShortDateString()))
    $TMPModuleContent = (get-content -path $IPAModuleFile).replace("%builddate%",($script:BuildingDate.ToShortDateString()))
    if (($script:objEnv.api_version) -and ($script:objEnv.jsonrpc_uri)) {
        $TMPModuleContent = $TMPModuleContent.replace("%api_version%",$script:objEnv.api_version)
        $TMPModuleContent = $TMPModuleContent.replace("%jsonrpc_uri%",($script:objEnv.jsonrpc_uri -replace ("/ipa/json","")))
    } else {
        throw "API Version missing in IPA Env file"
    }
    If ($BuilderName) {
        $TMPModuleContent = $TMPModuleContent.replace("%buildername%",$BuilderName)
        $TMPManifestContent = $TMPManifestContent.replace("%buildername%",$BuilderName)
    } Else {
        $TMPModuleContent = $TMPModuleContent.replace("%buildername%","N/A")
        $TMPManifestContent = $TMPManifestContent.replace("%buildername%","N/A")
    }
    If ($Version) {
        $TMPModuleContent = $TMPModuleContent.replace("%version%",$Version)
        $TMPManifestContent = $TMPManifestContent.replace("%version%",$Version)
    } Else {
        $TMPModuleContent = $TMPModuleContent.replace("%version%","0-N/A")
        $TMPManifestContent = $TMPManifestContent.replace("%version%","0-N/A")
    }
    If ($VersionComment) {
        $TMPModuleContent = $TMPModuleContent.replace("%versioncomment%",$VersionComment)
        $TMPManifestContent = $TMPManifestContent.replace("%versioncomment%",$VersionComment)
    } Else {
        $TMPModuleContent = $TMPModuleContent.replace("%versioncomment%","N/A")
        $TMPManifestContent = $TMPManifestContent.replace("%versioncomment%","N/A")
    }
    $TMPModuleContent | set-content -path $IPAModuleFile | out-null
    $TMPModuleContent = $null
    $script:IPACmdlandAlias = @()
    if (test-path $IPASchemaRef) {
        $script:ObjSchema = Import-Clixml -Path $IPASchemaRef
        $Script:APIsName = ($script:ObjSchema | Get-Member -Type Property,NoteProperty | Select-Object -Property Name).name
        [System.Double]$Script:ProgressCount = 0
        Write-Information "$($Script:APIsName.count) APIs found in FreeIPA Schema"
        Write-Information "Starting generation of Powershell Code for $($Script:APIsName.count) functions" 
        foreach ($APIName in $Script:APIsName) {
            $Script:ProgressCount = $Script:ProgressCount + 1
            Write-Progress -Activity "Building PowerShell Code Function in Progress" -Status "Building code and bindings for $($APIName) API" -PercentComplete (($Script:ProgressCount/$Script:APIsName.count)*100)
            $script:IPACmdlandAlias += Get-IPAPCmdletName $script:ObjSchema."$($APIName)"
            $APINameBindings = Get-IPACmdletBinding -InfoType "BindingValue" -inputSchemaObject $script:ObjSchema."$($APIName)"
            $APIBindingConditions = Get-IPACmdletBinding -InfoType "BindingCondition" -inputSchemaObject $script:ObjSchema."$($APIName)"
            ## Start Function
            ## Function Help Header
            add-content -path $IPAModuleFile -Value "function Invoke-FreeIPAAPI$($APIName) {
        <#
            .DESCRIPTION"

            if ($script:ObjSchema.$APINAME.doc) {
                add-content -path $IPAModuleFile -Value " $($script:ObjSchema.$APINAME.doc.replace('--','-'))"
            }
            foreach ($Binding in $APINameBindings) {
                add-content -path $IPAModuleFile -Value " .PARAMETER $($Binding.Variable)"
                if ($Binding.Help) {
                    add-content -path $IPAModuleFile -Value " $($Binding.Help.replace('--','-'))"
                }
            }
            add-content -path $IPAModuleFile -Value " #>"
            ## Function Param Header
            add-content -path $IPAModuleFile -Value " [CmdletBinding()]
        [OutputType([psobject])]
            Param("

            foreach ($Binding in $APINameBindings) {
                add-content -path $IPAModuleFile -Value " $($Binding.Parameter)
                    $($Binding.Validation)"

                $line = " " + $Binding.Variableandtype + ","
                add-content -path $IPAModuleFile -Value $line
            }
            add-content -path $IPAModuleFile -Value " [parameter(Mandatory=`$false)]
                    [switch]`$FullResultsOutput"

            add-content -path $IPAModuleFile -Value " )"
            ## Function body
            add-content -path $IPAModuleFile -Value " process {"
            add-content -path $IPAModuleFile -Value " If (`$version) {
                `$ObjParams = New-Object psobject -property @{
                    version = `$version
                }
            } else {
                `$ObjParams = New-Object psobject -property @{
                    version = `$global:FreeIPAAPIServerConfig.ClientVersion
                }
            }"

            foreach ($Binding in $APINameBindings) {
                if ($Binding.Variableandtype -like "*SecureString*") {
                    $line = " if ($" + $Binding.variable + ") { $" + $Binding.variable + " = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($" + $Binding.variable + ")) }"
                    add-content -path $IPAModuleFile -Value $line
                }
            }
            foreach ($condition in $APIBindingConditions) {
                add-content -path $IPAModuleFile -Value $condition.OptionCondition
            }
            if ($script:ObjSchema."$($APIName)".takes_args.cli_name) {
                $argparams =  ($script:ObjSchema."$($APIName)".takes_args.cli_name | ForEach-Object {'${0}' -f $_}) -join ","
            } else {
                $argparams = ""
            }
            add-content -path $IPAModuleFile -Value " `$JsonObject = New-Object psobject -property @{
                id = 0
                method = `"$($APIName)/1`"
                params = @(@($($argparams)),`$ObjParams)
            }"

            add-content -path $IPAModuleFile -Value " if (!(`$FullResultsOutput.IsPresent)) {
                (Invoke-FreeIPAAPI `$JsonObject).result.result
            } else {
                Invoke-FreeIPAAPI `$JsonObject
            }"

            add-content -path $IPAModuleFile -Value " }"
            add-content -path $IPAModuleFile -Value "}"
            ## End of Function
        }
        ## Create Powershell Alias
        foreach ($cmdlet in $IPACmdlandAlias) {
            if ($cmdlet.CmdletDoc) {
                $line = "New-alias -Name $($cmdlet.PWshAliasName) -Value $($cmdlet.PWshFunctionName) -Description " + '"' + "$(($cmdlet.CmdletDoc.split([Environment]::NewLine, [StringSplitOptions]::RemoveEmptyEntries))[0])" + '"'
            } else {
                $line = "New-alias -Name $($cmdlet.PWshAliasName) -Value $($cmdlet.PWshFunctionName)"
            }
            add-content -path $IPAModuleFile -Value $line
        }
        add-content -path $IPAModuleFile -Value 'New-Alias -Name Set-IPACredentials -value Set-FreeIPAAPICredentials -Description "Set your IPA API Credential for authentication purpose if your using non Kerberos authentication"'
        add-content -path $IPAModuleFile -Value 'New-Alias -Name Import-IPACrendentials -Value Import-FreeIPAAPICrendentials -Description "Import your IPA API Credential from a local file hosted in your Windows Profile"'
        add-content -path $IPAModuleFile -Value 'New-Alias -Name Set-IPAServerConfig -Value Set-FreeIPAAPIServerConfig -Description "Set IPA server URL and client version"'
        add-content -path $IPAModuleFile -Value 'New-Alias -Name Connect-IPA -value Get-FreeIPAAPIAuthenticationCookie -Description "Get your authentication cookie and save it to be used with all cmdlets/functions"'
        ## Export Alias and Functions
        $AllFunctions = ($script:IPACmdlandAlias.PWshFunctionName -join ',') + ",Set-FreeIPAAPICredentials,Import-FreeIPAAPICrendentials,Set-FreeIPAAPIServerConfig,Get-FreeIPAAPIAuthenticationCookie"
        $AllAlias = ($script:IPACmdlandAlias.PWshAliasName -join ',') + ",Set-IPACredentials,Import-IPACrendentials,Set-IPAServerConfig,Connect-IPA"
        add-content -path $IPAModuleFile -Value "Export-ModuleMember -Function $($AllFunctions)"
        add-content -path $IPAModuleFile -Value "Export-ModuleMember -Alias $($AllAlias)"
        ## Update functions and alias in Manifest
        $ManifestFunctions = ($AllFunctions -split "," | ForEach-Object {"'{0}'" -f $_}) -join ","
        $ManifestAlias = ($AllAlias -split "," | ForEach-Object {"'{0}'" -f $_}) -join ","
        $TMPManifestContent = $TMPManifestContent.replace("%functions%",$ManifestFunctions)
        $TMPManifestContent = $TMPManifestContent.replace("%alias%",$ManifestAlias)
        $TMPManifestContent | set-content -path $IPAManifestFile | out-null
        # Output object for summary
        New-Object psobject -Property @{
            BuildingDate = $script:BuildingDate
            ModuleFileName = $IPAModuleFile
            ManifestFileName = $IPAManifestFile
            Version = $Version
            Changelog = $VersionComment
            Builder = $BuilderName
            Functions = $AllFunctions -split ","
            Alias = $AllAlias -split ","
        }
    }
}

New-Alias -Name Get-IPAJsonMetadata -Value Invoke-FreeIPAAPIJson_Metadata -Description "Get metadata information used by IPA API Web browsing page"
New-Alias -Name Connect-IPA -value Get-FreeIPAAPIAuthenticationCookie -Description "Get your authentication cookie and save it to be used with all cmdlets/functions"
New-Alias -Name Disconnect-IPA -value Invoke-FreeIPAAPISessionLogout -Description "Remove your authentication cookie and close remote server session"
New-Alias -Name Get-IPAEnvironment -Value Invoke-FreeIPAAPIEnv -Description "Get all IPA environment information"
New-Alias -Name Set-IPAServerConfig -Value Set-FreeIPAAPIServerConfig -Description "Set IPA server URL and client version"
New-Alias -Name Import-IPACrendentials -Value Import-FreeIPAAPICrendentials -Description "Import your IPA API Credential from a local file hosted in your Windows Profile"
New-Alias -Name Set-IPACredentials -value Set-FreeIPAAPICredentials -Description "Set your IPA API Credential for authentication purpose if your using non Kerberos authentication"

Export-ModuleMember -Function Invoke-FreeIPAAPIJson_Metadata, Get-FreeIPAAPIAuthenticationCookie, Invoke-FreeIPAAPISessionLogout, Invoke-FreeIPAAPIEnv, Export-IPASchema, Export-IPAEnv, 
                                Publish-IPAModule, Get-IPAPCmdletName, Get-IPACmdletBinding, Get-IPACmdletBindingValue, Get-IPABindingCondition, Set-FreeIPAAPICredentials, Import-FreeIPAAPICrendentials, 
                                Set-FreeIPAAPIServerConfig, Get-ScriptDirectory
Export-ModuleMember -Alias Get-IPAJsonMetadata, Connect-IPA, Disconnect-IPA, Get-IPAEnvironment, Set-IPAServerConfig, Import-IPACrendentials, Set-IPACredentials