Closed jhainau closed 3 years ago
Hey @jhainau,
I just tried this myself, using the same versions, but for me Import-Module ActiveDirectory
works under IIS each time with just the warning:
WARNING: Module ActiveDirectory is loaded in Windows PowerShell using WinPSCompatSession remoting session; please note
that all input and output of commands from this module will be deserialized objects. If you want to load this module into
PowerShell please use 'Import-Module -SkipEditionCheck' syntax.
I've got IIS running, the software version you mentioned, and RSAT AD DS/LDS Tools installed.
Would you be able to give a snippet of the script where you import AD, and of your web.config?
Server.ps1:
Import-Module -Name Pode
Import-Module -Name Pode.Web
import-Module -Name activeDirectory
#import-Module -Name PCPerson #installed from custom gallery
[System.Net.ServicePointManager]::SecurityProtocol = $([System.Net.ServicePointManager]::SecurityProtocol), [System.Net.SecurityProtocolType]::Tls12
Start-PodeServer {
Add-PodeEndpoint -Address * -Port 80 -Protocol Http
Import-PodeModule -name ActiveDirectory
$BaseURL = "$([System.Net.Dns]::GetHostEntry($Env:ComputerName).HostName)"
Set-PodeState -Name BaseURL -Value $BaseURL | Out-Null
New-PodeAuthScheme -Basic | Add-PodeAuth -Name 'Login' -Sessionless -ScriptBlock {
param($username, $password)
<# # here you'd check a real user storage, this is just for example
if ($username -eq 'morty' -and $password -eq 'pickle') {
return @{
User = @{
'ID' ='M0R7Y302'
'Name' = 'Morty';
'Type' = 'Human';
}
}
}
#>
$SecPassword = ConvertTo-SecureString -String $password -AsPlainText -Force
$userCred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $username, $SecPassword
$ADObject = $(try { Get-ADUser -Properties memberof,SID,SamAccountName,UserPrincipalName -Identity $username -Credential $userCred -ErrorAction Stop }Catch { $false })
if ($ADObject -ne $false) {
# success
# here you store some data that only gets passed server side and never over the wire
#* I'm using this as an area to store authorizations for
#* services that the user has permissions to...
# Person is the name of a HR system we use. it has it's own module
#$personBasics = Get-PersonLookup -Source person -loginName $username
# $assignedSystems = Get-PersonUserSystem -Identity $username -Source person -listall | Where-Object { [string]::IsNullOrEmpty($_.stopDt) }
#$assignedRoles = Get-PersonAssignedRole -TargetUser $username -Source person | Where-Object { [string]::IsNullOrEmpty( $_.endDt) }
return @{ User = [ordered]@{
'ID' = $ADObject.SID
'Name' = $ADObject.SamAccountName
'UPN' = $ADObject.UserPrincipalName
'AdGroups' = $ADObject.memberof
'cred' = $userCred
#"Systems" = $assignedSystems.system
#"roles" = $assignedRoles
#'userInfo' = $personBasics
}
}
}
# authentication failed
return $null
}
Add-PodeRoute -Method Get -path '/UserInfo' -Authentication 'Login' -ScriptBlock {
#param ($e)
$result = $WebEvent.auth.user
$result.remove('cred')
$html = @"
<!DOCTYPE HTML>
<html>
<head>
<title>
Custom API Service: UserInfo
</title>
<body style="text-align:center;" id="body">
<h1 style="color:green;">
User Information
</h1>
<p id="GFG_UP" style="font-size: 15px; font-weight: bold;">
</p>
<p align="left">
<pre align="left">
$($result |ConvertTo-Json -Depth 5)
</pre>
</p>
<br><br>
<!-- other stuff could go here-->
</body>
</html>
"@
Write-PodeHtmlResponse -Value $html
}
# GET request for web page on "localhost/"
Add-PodeRoute -Method Get -Path '/' -ScriptBlock {
Write-PodeTextResponse -value 'hello world'
}
}
Web.config:
<configuration>
<location path="." inheritInChildApplications="false">
<system.webServer>
<handlers accessPolicy="Read, Execute, Script">
<remove name="WebDAV" />
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
<remove name="ExtensionlessUrl-Integrated-4.0" />
<add name="ExtensionlessUrl-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
<modules>
<remove name="WebDAVModule" />
</modules>
<aspNetCore processPath="pwsh.exe" arguments=".\server.ps1" stdoutLogEnabled="true" stdoutLogFile=".\logs\stdout" hostingModel="OutOfProcess" />
<security>
<authorization>
<remove users="*" roles="" verbs="" />
<add accessType="Allow" users="*" verbs="GET,HEAD,POST,PUT,DELETE,DEBUG,OPTIONS" />
</authorization>
</security>
</system.webServer>
</location>
</configuration>
Hey @jhainau,
Thanks!
Import-PodeModule
not working makes sense here, as the way it works it'll be trying to find the ActiveDirectory module under /PowerShell
not /WindowsPowerShell
, thus failing. Just Import-Module
like you have at the top is the better way to go 😃
With that, and removing the Import-PodeModule
, the site starts in IIS without error.
Now, if I try to hit /UserInfo
, I start getting a different error:
Creating a new session for implicit remoting of "Get-ADUser" command...
But I was able to get around this by change the Get-ADUser
call to the following, and by moving the ActiveDirectory import as well:
$ADObject = Invoke-Command -ScriptBlock {
param($username)
Import-Module -Name ActiveDirectory
try {
Get-ADUser -Properties memberof,SID,SamAccountName,UserPrincipalName -Identity $username -ErrorAction Stop
}
catch {
$false
}
} -ArgumentList $username
Then /UserInfo
worked and returned the HTML with user information.
This was the full script I had in the end:
Import-Module -Name Pode
[System.Net.ServicePointManager]::SecurityProtocol = $([System.Net.ServicePointManager]::SecurityProtocol), [System.Net.SecurityProtocolType]::Tls12
Start-PodeServer {
Add-PodeEndpoint -Address * -Port 80 -Protocol Http
$BaseURL = "$([System.Net.Dns]::GetHostEntry($Env:ComputerName).HostName)"
Set-PodeState -Name BaseURL -Value $BaseURL | Out-Null
New-PodeAuthScheme -Basic | Add-PodeAuth -Name 'Login' -Sessionless -ScriptBlock {
param($username, $password)
$SecPassword = ConvertTo-SecureString -String $password -AsPlainText -Force
$userCred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $username, $SecPassword
$ADObject = Invoke-Command -ScriptBlock {
param($username)
Import-Module -Name ActiveDirectory
try {
Get-ADUser -Properties memberof,SID,SamAccountName,UserPrincipalName -Identity $username -ErrorAction Stop
}
catch {
$false
}
} -ArgumentList $username
if ($ADObject -ne $false) {
return @{ User = [ordered]@{
'ID' = $ADObject.SID
'Name' = $ADObject.SamAccountName
'UPN' = $ADObject.UserPrincipalName
'AdGroups' = $ADObject.memberof
'cred' = $userCred
}
}
}
# authentication failed
return $null
}
Add-PodeRoute -Method Get -path '/UserInfo' -Authentication 'Login' -ScriptBlock {
$result = $WebEvent.auth.user
$result.remove('cred')
$html = @"
<!DOCTYPE HTML>
<html>
<head>
<title>
Custom API Service: UserInfo
</title>
<body style="text-align:center;" id="body">
<h1 style="color:green;">
User Information
</h1>
<p id="GFG_UP" style="font-size: 15px; font-weight: bold;">
</p>
<p align="left">
<pre align="left">
$($result |ConvertTo-Json -Depth 5)
</pre>
</p>
<br><br>
</body>
</html>
"@
Write-PodeHtmlResponse -Value $html
}
# GET request for web page on "localhost/"
Add-PodeRoute -Method Get -Path '/' -ScriptBlock {
Write-PodeTextResponse -value 'hello world'
}
}
Hope that helps! :)
I followed your instructions, but get the same results, unable to load the Active directory module from IIS... but works outside of iis in both pwsh ad Windows PowerShell I suspect I am not setting up the IIS portion correctly as the instructions were ... less than thorough... I am not well versed with IIS... I may just need to set my instance up as a service using NSSM... but it is also a little lacking in thorough instructions... not to mention, NSSM has not been updated in a few years... but seams to be generally well liked and trusted. sorry for my lack of knowledge in these areas. on a side note... the reason I was using the "-credential" in the get-aduser bit was because I assume that if you have a valid ad account and password, it should be able to return some info the associated ad account... that was how I was performing the authentication... if the ad and password combo failed to return the info for the requesting account, then I assumed that the password or userID was incorrect
There isn't anything special I really do with IIS when setting up the site; I just install the software from the docs page, create a basic website and binding, and point it to the directory my script is in. When you say it's lacking, is there some extra steps you had to do in order to get IIS to work? Something we could add to the docs, or is it flesh out the part where it says "Create a site in IIS and add a binding"?
For NSSM, it's there because it is just commonly used to run things like PowerShell as a Windows Service. You don't have to use NSSM, if you've a preferred way of doing it using another tool then that's fine - maybe we could even document it here. Even a Scheduled Task is fine 😛
Loading the module in a main pwsh terminal is different to running it in IIS, when in IIS it is running in pwsh, but in an almost background remote session. A quick Google of the error AD gives when loading suggests other things that could be causing it to fail:
I would says it more an issue outside of IIS, preventing it from loading; as if you take the AD module/logic out and your server loads fine, then that's IIS done and working.
As for -Credential
missing that was just an artifact from my end, as I was originally testing with a PSSession. You can just add the $userCreds
to the Invoke-Command -ArgumentList
; I just forgot to put it back in.
Just curious, are you running the IIS App Pool using a local (non-domain) account maybe?
Yes, to the first question... Fleshing out the "Create a site in IIS and add binding" would be immensely helpful... so sorry,
as for NSSM... this does seem to be the Service Manager of choice - even in my organization... I was just surprised that such a popular tool would be years since the last update.
as for IIS App Pool with local account... Maybe? My day job is Desktop Support. I learned PowerShell out of necessity to automate the mind numbing repetitive tasks I perform. I only just started looking into IIS and NSSM in support of my project that uses Pode.
You have been very patient and understanding, Thank you :-)
I'll look at getting that part fleshed out then! 😃
What @RobinBeismann said is likely the cause; if you followed the docs and let the site create the app-pool, and changed nothing else, the app-pool could be running as the ApplicationPoolIdentiy user. Changing that to a domain user should hopefully fix it.
I do it out of habit so much, I didn't even think about it, oops!
Got it! I had the IIS settings not quite right... I forgot to Exclude the logs folder so that it didn't restart the server on every request, I followed the advice from you and @RobinBeismann with regards to starting the app pool with domain credentials, and the last thing was in IIS Authentication for my site, edit Basic Authentication and enter my domain in the Default domain. everything is working as expected... for now Thank you so much!
Is there any chance to get the -UseUPN flag? I have a scenario where i have 100+ different @doamin
Question
I'm putting this as a question, because I'm sure I'm just not doing it right... I am using the latest Pode module (2.2.3), powershell core 7.1.3, dotnet-hosting-5.0.6-win When I have the line "Import-module ActiveDirectory" or Import-podemodule activedirectory, it fails in iis, but not when run directly in powershell. both display the warning:
but IIS has the following in the stdout log: using import-module activedirectory
IIS stdoutlog using import-podemodule activedirectory