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 |