Build-FreeIPAModule.psm1
# # Created by: lucas.cueff[at]lucas-cueff.com # Build by : Lucas Cueff # v0.7 : First Release # v0.8 : Add multi config file as requested by baldator + Fix securestring issue reported by nadinezan + add proxy management to connect to IPA # # Released on: 22/02/2020 # #'(c) 2018-2020 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 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 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 + ") { [string]$" + $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"' add-content -path $IPAModuleFile -Value 'New-Alias -Name Set-IPAProxy -value Set-FreeIPAProxy -Description "Set a web proxy to be used to connect your FreeIPA server APIs"' ## Export Alias and Functions $AllFunctions = ($script:IPACmdlandAlias.PWshFunctionName -join ',') + ",Set-FreeIPAAPICredentials,Import-FreeIPAAPICrendentials,Set-FreeIPAAPIServerConfig,Get-FreeIPAAPIAuthenticationCookie,Set-FreeIPAProxy" $AllAlias = ($script:IPACmdlandAlias.PWshAliasName -join ',') + ",Set-IPACredentials,Import-IPACrendentials,Set-IPAServerConfig,Connect-IPA,Set-IPAProxy" 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 "," } } } 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" } try { if ($json) { if ($global:FreeIPAProxyParams) { $params = $global:FreeIPAProxyParams.clone() if (!$params.UseBasicParsing){$params.add('UseBasicParsing', $true)} } Else { $params = @{} $params.add('UseBasicParsing', $true) } $params.add('URI', "$($global:FreeIPAAPIServerConfig.ServerURL)/ipa/session/json") $params.add('WebSession', $global:FreeIPASession) $params.add('Body',$json) $params.add('Method','POST') $params.add('ContentType','application/json') $params.add('Headers',@{"Referer"="$($global:FreeIPAAPIServerConfig.ServerURL)/ipa/session/json"}) Invoke-RestMethod @params } } catch { write-verbose -message "Error Type: $($_.Exception.GetType().FullName)" write-verbose -message "Error Message: $($_.Exception.Message)" write-verbose -message "HTTP error code:$($_.Exception.Response.StatusCode.Value__)" write-verbose -message "HTTP error message:$($_.Exception.Response.StatusDescription)" $PSCmdlet.ThrowTerminatingError($PSitem) } } } Function Set-FreeIPAProxy { <# .SYNOPSIS Set an internet proxy to use FreeIPA web api .DESCRIPTION Set an internet proxy to use FreeIPA web api .PARAMETER DirectNoProxy -DirectNoProxy Remove proxy and configure FreeIPA powershell functions to use a direct connection .PARAMETER Proxy -Proxy{Proxy} Set the proxy URL .PARAMETER ProxyCredential -ProxyCredential{ProxyCredential} Set the proxy credential to be authenticated with the internet proxy set .PARAMETER ProxyUseDefaultCredentials -ProxyUseDefaultCredentials Use current security context to be authenticated with the internet proxy set .PARAMETER AnonymousProxy -AnonymousProxy No authentication (open proxy) with the internet proxy set .OUTPUTS none .EXAMPLE Remove Internet Proxy and set a direct connection C:\PS> Set-FreeIPAProxy -DirectNoProxy .EXAMPLE Set Internet Proxy and with manual authentication $credentials = get-credential C:\PS> Set-FreeIPAProxy -Proxy "http://myproxy:8080" -ProxyCredential $credentials .EXAMPLE Set Internet Proxy and with automatic authentication based on current security context C:\PS> Set-FreeIPAProxy -Proxy "http://myproxy:8080" -ProxyUseDefaultCredentials .EXAMPLE Set Internet Proxy and with no authentication C:\PS> Set-FreeIPAProxy -Proxy "http://myproxy:8080" -AnonymousProxy #> [cmdletbinding()] Param ( [Parameter(Mandatory=$false,ValueFromPipelineByPropertyName=$false)] [switch]$DirectNoProxy, [Parameter(Mandatory=$false,ValueFromPipelineByPropertyName=$true)] [ValidateScript({$_ -match "(http[s]?)(:\/\/)([^\s,]+)"})] [string]$Proxy, [Parameter(Mandatory=$false,ValueFromPipelineByPropertyName=$false)] [Management.Automation.PSCredential]$ProxyCredential, [Parameter(Mandatory=$false,ValueFromPipelineByPropertyName=$false)] [Switch]$ProxyUseDefaultCredentials, [Parameter(Mandatory=$false,ValueFromPipelineByPropertyName=$false)] [Switch]$AnonymousProxy ) if ($DirectNoProxy.IsPresent){ $global:FreeIPAProxyParams = $null } ElseIf ($Proxy) { $global:FreeIPAProxyParams = @{} $FreeIPAProxyParams.Add('Proxy', $Proxy) if ($ProxyCredential){ $FreeIPAProxyParams.Add('ProxyCredential', $ProxyCredential) If ($FreeIPAProxyParams.ProxyUseDefaultCredentials) {$FreeIPAProxyParams.Remove('ProxyUseDefaultCredentials')} } Elseif ($ProxyUseDefaultCredentials.IsPresent){ $FreeIPAProxyParams.Add('ProxyUseDefaultCredentials', $ProxyUseDefaultCredentials) If ($FreeIPAProxyParams.ProxyCredential) {$FreeIPAProxyParams.Remove('ProxyCredential')} } ElseIf ($AnonymousProxy.IsPresent) { If ($FreeIPAProxyParams.ProxyUseDefaultCredentials) {$FreeIPAProxyParams.Remove('ProxyUseDefaultCredentials')} If ($FreeIPAProxyParams.ProxyCredential) {$FreeIPAProxyParams.Remove('ProxyCredential')} } } } 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()] [string]$ConfigName, [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 = 'Manage-FreeIPA' if ($ConfigName) { $ConfigName = "Manage-FreeIPA-$($ConfigName).xml" } else { $ConfigName = 'Manage-FreeIPA.xml' } if (!$home -and $env:userprofile) { $global:home = $env:userprofile } if (!(Test-Path -Path "$($global:home)\$FolderName")) { New-Item -ItemType directory -Path "$($global:home)\$FolderName" | Out-Null } if (test-path "$($global:home)\$FolderName\$ConfigName") { Remove-item -Path "$($global:home)\$FolderName\$ConfigName" -Force | out-null } $ObjConfigFreeIPA | Export-Clixml "$($global:home)\$FolderName\$ConfigName" } } } } Function Import-FreeIPAAPICrendentials { [CmdletBinding()] Param( [parameter(Mandatory=$false,ValueFromPipelineByPropertyName=$true)] [ValidateNotNullOrEmpty()] [string]$ConfigName, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [securestring]$MasterPassword ) process { $FolderName = 'Manage-FreeIPA' if ($ConfigName) { $ConfigName = "Manage-FreeIPA-$($ConfigName).xml" } else { $ConfigName = 'Manage-FreeIPA.xml' } if (!$home -and $env:userprofile) { $global:home = $env:userprofile } if (!(Test-Path "$($global:home)\$($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 "$($global:home)\$($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=$false,ValueFromPipelineByPropertyName=$true)] [ValidateScript({$_ -match "(http[s]?)(:\/\/)([^\s,]+)"})] [String]$URL, [parameter(Mandatory=$false)] [ValidateNotNullOrEmpty()] [String]$ClientVersion ) process { if ($URL) { $global:FreeIPAAPIServerConfig = @{ ServerURL = $URL } } else { $global:FreeIPAAPIServerConfig = @{ ServerURL = "https://ipa.demo1.freeipa.org" } } if ($ClientVersion) { $global:FreeIPAAPIServerConfig.add('ClientVersion',$ClientVersion) } else { $global:FreeIPAAPIServerConfig.add('ClientVersion',"2.29") } } } 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) } if ($global:FreeIPAProxyParams) { $params = $global:FreeIPAProxyParams.clone() if (!$params.UseBasicParsing){$params.add('UseBasicParsing', $true)} } Else { $params = @{} $params.add('UseBasicParsing', $true) } $params.add('URI', "$($global:FreeIPAAPIServerConfig.ServerURL)/ipa/session/login_password") $params.add('Session', 'FunctionFreeIPASession') $params.add('Body',$BSTRCredentials) $params.add('Method','POST') $FreeIPALogin = Invoke-WebRequest @params } catch { write-verbose -message "Error Type: $($_.Exception.GetType().FullName)" write-verbose -message "Error Message: $($_.Exception.Message)" write-verbose -message "HTTP error code:$($_.Exception.Response.StatusCode.Value__)" write-verbose -message "HTTP error message:$($_.Exception.Response.StatusDescription)" $PSCmdlet.ThrowTerminatingError($PSitem) } $global:FreeIPASession = $FunctionFreeIPASession $FreeIPALogin } } 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" New-Alias -Name Set-IPAProxy -value Set-FreeIPAProxy -Description "Set a web proxy to be used to connect your FreeIPA server APIs" 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, Set-FreeIPAProxy Export-ModuleMember -Alias Get-IPAJsonMetadata, Connect-IPA, Disconnect-IPA, Get-IPAEnvironment, Set-IPAServerConfig, Import-IPACrendentials, Set-IPACredentials, Set-IPAProxy |