SharePoint search service setup with desired state configuration error - powershell

I'm configuring a SharePoint 2013 deployment using Desired State Configuration (DSC). I have configured several services to be provisioned using DSC, but I am having trouble getting search config to work. The following command fails in the context of DSC, but works fine when running with the exact same parameters in a normal PowerShell window:
function Set-TargetResource
{
...
$searchApp = New-SPEnterpriseSearchServiceApplication -Name $searchAppName `
-DatabaseServer $dbServer `
-DatabaseName $searchDB `
-ApplicationPool $pool `
-AdminApplicationPool $adminPool `
-Partitioned:([bool]::Parse($partitioned))
If (!$?) {
Throw " - An error occurred creating the $searchAppName application."
}
...
Other SharePoint cmdlets are working fine from within DSC. I know DSC runs in the context of "NT AUTHORITY\SYSTEM" - is this causing the problem for some SharePoint PowerShell cmdlets? - if so, how could search configuration still be achieved within the context of DSC?

You need to use different set of credentials that got the SP farm access. See xWindowsProcess DSC resource for an example. This is available as a part of the DSC resource kit.

Related

Installing ADFS via Terraform fails

I am converting an ADFS demo environment I have from vagrant to terraform. Everything works fine until I get to running:
Install-AdfsFarm `
-CertificateThumbprint:"$certThumbprint" `
-FederationServiceDisplayName:"adfs.test.my-poc.com" `
-FederationServiceName:"adfs.test.my-poc.com" `
-ServiceAccountCredential:$credential
I get the following error:
Install-AdfsFarm : The service account credentials could not be verified.
null_resource.post-vm (remote-exec): At C:\provision\adfs2.ps1:48 char:1
When I log into to server with the same credentials terraform uses and run the script manually, it works fine. I don't think this is an ADFS specific error, as the following also fails in the script, but works if I log in:
(New-Object DirectoryServices.DirectoryEntry "",$username,$password).psbase.name -ne $null
Does anyone have any suggestions?
Thanks.

How do I update the IP whitelist for a staging slot via Azure Powershell from an Azure DevOps Release Pipeline?

I have an application hosted in Azure, and I use Azure DevOps to manage my build and release pipelines. As part of the release, I warm up the application by making a request to the root url (e.g. https://myapp.azurewebsites.net). In order to make this request I must first make sure the hosted build agent running the deployment has access to that url (or I will get a 403). I have written a short powershell script to achieve this, and put it in an Azure Powershell task. It adds the IP of the build agent to the IpSecurityConfiguration of the app service. So far so good. It works perfectly for apps that are just apps. Where it falls down is when I try to use it against a staging environment. When we release to production we first push the code to a staging slot, then flip it over to live when we've run our tests and made sure everything is good. The powershell script that correctly handles the IpSecurityConfiguration for the app services does not work on the staging slot. To access a staging slot, we use myappname/slots/staging for the variable $(WebApiName), normally it would just be the name of the app service itself. Again, this works perfectly if I run the script from my local environment, it only fails in the pipeline. The code is below:
# Whitelist Azure Agent IPs
$agentIP = Invoke-RestMethod http://ipinfo.io/json | Select -exp ip
Write-Host "Connecting to Azure"
$APIVersion = ((Get-AzureRmResourceProvider -ProviderNamespace Microsoft.Web).ResourceTypes | Where-Object ResourceTypeName -eq sites).ApiVersions[0]
Write-Host "API Version is $APIVersion. Getting web app config for $(WebApiName) in $(ResourceGroupName)"
$WebApiConfig = (Get-AzureRmResource -ResourceType Microsoft.Web/sites/config -ResourceName $(WebApiName) -ResourceGroupName $(ResourceGroupName) -ApiVersion $APIVersion)
Write-Host "Got web app config: $WebApiConfig"
$webIP = [PSCustomObject]#{
ipAddress = "$agentIP/32";
action = "Allow";
tag = 'Default';
priority = 300;
name = $agentIP.ToString();
description = $agentIP.ToString()
}
Write-Host "Adding $agentIP to security restrictions"
$WebApiConfig.Properties.ipSecurityRestrictions += $webIP
Write-Host "Updating security restrictions"
# update app restrictions, do not prompt for confirmation
$result = Set-AzureRmResource -ResourceId $WebApiConfig.ResourceId -Properties $WebApiConfig.Properties -ApiVersion $APIVersion -Force
To muddy the water somewhat, I can get the exact same code to work perfectly with the staging slot locally by changing
$WebApiConfig = (Get-AzureRmResource -ResourceType Microsoft.Web/sites/config -ResourceName $(WebApiName) -ResourceGroupName $(ResourceGroupName) -ApiVersion $APIVersion)
to
$WebApiConfig = (Get-AzureRmResource -ResourceType Microsoft.Web/sites -ResourceName $(WebApiName)/config -ResourceGroupName $(ResourceGroupName) -ApiVersion $APIVersion)
but this doesn't work in the Azure Powershell task. Instead I can't deploy to any environment because the task fails while trying to access IpSecurityRestrictions on the $WebApiConfig object. The exception is "Exception setting "ipSecurityRestrictions": "The property 'ipSecurityRestrictions' cannot be found on this object. Verify that the property exists and can be set."
As I said earlier, if I run the script in exactly this form locally, it works perfectly. Obviously I have to manually replace the variables that come from the build pipeline, but otherwise there is no difference between code that works exactly as I want it to on my local machine and code that fails in the release. You can verify this by swapping out $(WebApiName) for a valid app service name and $(ResourceGroupName) for the resource group that app service is in. I put a line in about halfway down that outputs $WebApiConfig so that I can see what it is, and on my local machine I see a valid object, while in the output of the task I get nothing. The line just says "Got web app config:"
Anyone got any ideas?
I've tried changing the version of powershell used by the task to
match the version I've got.
I've tried using the preview version of the task (v4, otherwise I've been using v3).
I've tried every permutation of /sites/config everywhere I can think of in the call to Get-AzureRmResource (since that was what allowed it to work locally on the slot).
Just one final thing in case anyone wonders. I'm doing it this way instead of whitelisting all the IPs in Microsoft's list (https://www.microsoft.com/en-us/download/confirmation.aspx?id=41653) for two reasons, firstly it's a lot easier to maintain a short list of our own IPs, and secondly there seems to be a bug somewhere in the way Azure handles those CIDR definitions because IPs that are categorically in those ranges are frequently blocked during our deployments even when we have the entire file whitelisted. This way I just add whichever IP is currently being used dynamically to the whitelist, and remove it after we're done. Assuming I can get it to work...
Finally figured out the solution to this. In order to work with slots the resource type has to be subtly different. This line works in an Azure Powershell task:
$WebApiConfig = (Get-AzureRmResource -ResourceType Microsoft.Web/sites/slots/config -ResourceName $(WebApiName) -ResourceGroupName $(ResourceGroupName) -ApiVersion $APIVersion)
Posting in case it helps anyone else with the same issue. I can confirm that the approach I've taken works great in managing access to Azure sites by build agents, and saves a lot of messing around with Microsoft's build agent xml file.

Powershell Azure : The term 'Get-AutomationConnection' is not recognized as the name of a cmdlet, function, script file, or operable program

I am trying to connect to an Azure Run As connection, as part of a Powershell script that does a backup of a database.
This script attempts to call Get-AutomationConnection
As seen in the screenshot, Get-Module does return that Azure / Azure.Storage and AzureRM shows.
What module should I import in addition for this to work?
If you want to connect to an Azure Run As connection from Windows PowerShell, you should use New-AzureRmAutomationConnection.
$ConnectionAssetName = "AzureRunAsConnection"
$ConnectionFieldValues = #{"ApplicationId" = $Application.ApplicationId; "TenantId" = $TenantID.TenantId; "CertificateThumbprint" = $Cert.Thumbprint; "SubscriptionId" = $SubscriptionId}
New-AzureRmAutomationConnection -ResourceGroupName $ResourceGroup -AutomationAccountName $AutomationAccountName -Name $ConnectionAssetName -ConnectionTypeName AzureServicePrincipal -ConnectionFieldValues $ConnectionFieldValues
You are able to use the script to create the connection asset because when you create your Automation account, it automatically includes several global modules by default along with the connection type AzurServicePrincipal to create the AzureRunAsConnection connection asset.
Get-AutomationConnection runs in Azure runbook internally.
Please refer to connection assets in Azure Automation.
If you want similar functionality to runbooks on-premise, you can install AzureAutomationAuthoringToolkit. It will give you very similar functionality. I have one script that logs in using the service principal, whether it is running on-premise or in an Azure runbook. It uses the resources provided by AAATK when running on-premise, that simulate a runbook.
I did try using the version of Get-AutomationConnection that comes with the "Microsoft Monitoring agent" (Hybrid worker), but I have since read that it is different to the one that comes with AzureAutomationAuthoringToolkit, detailed in the "Known Issues" in the GitHub readme. I couldn't get it to work, so I reverted to AAATK's version.

Trouble setting up MSMQ ACL using PowerShell cmdlet

My MSMQ queue gets created by PowerShell DSC engine. I can see queues created. Since DSC engine runs from SYSTEM account, then queue owner also gets set to SYSTEM.
When I try to set MSMQ ACL from PowerShell console I constantly get following error:
PS C:\Users\Administrator.DOMAIN> whoami; Get-MsmqQueue queue1 | Set-MsmqQueueACL -UserName "Everyone" -Allow FullControl
DOMAIN\administrator
Set-MsmqQueueACL : Failed to set security descriptor. Error code: 3222143013
At line:1 char:50
+ whoami; Get-MsmqQueue incredipay_atm_processor | Set-MsmqQueueACL -UserName "Eve ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidResult: (FullControl:MessageQueueAccessRights) [Set-MsmqQueueACL], Win32Exception
+ FullyQualifiedErrorId : Failed to set security descriptor. Error code: 3222143013,Microsoft.Msmq.PowerShell.Commands.SetMSMQQueueACLCommand
I also can't set MSMQ ACL using custom DSC resource, which is basically doing the same thing only from SYSTEM account.
So the question is are there any way to set MSMQ permissions from within PowerShell DSC engine using Set-MSMQQueueACL cmdlet. Or at least if I'll be able to solve previously mentioned mentioned error, then maybe I'll be able to solve also DSC problem.
I'm running Windows 2012 and WMF 4.0.
Thanks in advance.
I did something similar recently and hit the same problem. You have to take ownership of the queue first (admin rights required), and then you can change the permissions.
Try these manual steps in the Computer Management snap-in first to check it solves your error, and then work out how to reproduce it via PowerShell.
Start -> Run -> compmgmt.msc
Expand "Computer management (Local) -> Services and Applications -> Message Queuing -> Private Queues"
Right click -> Properties -> Security -> Advanced -> Owner -> Other users or groups...
Enter your user name (DOMAIN\administrator)
Click OK, then OK again
You should now be able to edit security via script
I ended up writing some PInvoke code to take ownership of the queue using C#, which I compiled on the fly with Add-Type in PowerShell. I can't share it unfortunately as it's proprietary, but this question might give you some pointers:
How do I set the owner of a message queue?
P.S. error code 3222143013 is 0xC00E0025, which translates to MQ_ERROR_ACCESS_DENIED (see http://msdn.microsoft.com/en-us/library/ms700106%28v=vs.85%29.aspx)
I've managed to overcome this issue by using following code in my custom DSC resource:
$ScriptBlock={
param(
[String] $QueueName,
[String] $Username,
[String[]] $MessageQueueAccessRight,
[ValidateSet("Allow","Deny")]
[String] $MessageQueueAccessType
)
$params = #{}
$queue = Get-MSMQQueue -Name $QueueName
$params.Add("InputObject",$queue)
$params.Add("Username",$Username)
switch ($MessageQueueAccessType)
{
"Allow" {$params.Add("Allow","$MessageQueueAccessRight"); Break;}
"Deny" {$params.Add("Deny","$MessageQueueAccessRight"); Break;}
}
Set-MsmqQueueACL #params
}
Foreach($MessageQueueAccessRight in $MessageQueueAccessRights)
{
Invoke-Command -ScriptBlock $ScriptBlock -ComputerName . -Credential $DomainAdministratorCredential -ArgumentList $QueueName,$Username,$MessageQueueAccessRight,$MessageQueueAccessType
}
Of course it's necessary to use the same approach when MSMQ queue gets created by DSC. So MSMQ queue creation should be made by the same account, whose initially going to adjust ACLs.
To do this in DSC, you can run your command using different credentials by having your custom DSC resource take a [PSCredential] parameter.
To do this securely requires some significant changes to your DSC infrastructure. See my answer to this question: https://serverfault.com/questions/632390/protecting-credentials-in-desired-state-configuration-using-certificates/#632836
If you just want to test before making those changes, you can tell DSC to allow storing your credentials in plaintext using PSDscAllowPlainTextPassword = $true in your configuration data (see here for details).
I also created a custom DSC resource to setup/modify my MSMQ queues within my web farm. Since DSC runs as SYSTEM you must ensure that the SYSTEM account has access to create/modify MSMQ's on the node.
There is a way to have DSC run as an account. If that is the case then you have to ensure that you are passing in that account when attempting to create/modify your MsmqQueue.
I understand I am responding to an old thread. But someone else in the near future may be facing the same issue and come across this thread.
Enjoy & Good Luck!

Orchestrator won't run PowerShell Cloud Exchange task

I'm having a problem getting a PowerShell script which queries objects in a cloud-based Exchange resource to work in an Orchestrator runbook.
The PowerShell script (which works correctly from my desktop computer's command line and when stepping through it in ISE) sets up a remote management session to the cloud and looks like this:
try
{
$user = "username#domain.com"
$pword = convert-toSecureString -string "password" -asplaintext -force
$creds = new-object -typename system.management.automation.pscredential -argumentlist $user, $pword
$o365 = new-pssession -configurationname Microsoft.Exchange -connectionuri https://ps.outlook.com -credential $creds -authentication basic - allowredirection
import-pssession $o365 -allowclobber -prefix o365
get-o365Mailbox 'Doe, John'
}
catch
{
throw $_.exception
}
As I mentioned, it runs fine when I step through it in the editor on my desktop but when executed inside the Orchestrator runbook it fails on the "import-pssession" command (because the $o365 is never set).
I've taken the PowerShell script and run it manually on the actual runbook server and it works there as well as it does on my own desktop -- it's only when run inside of an Orchestrator runbook that it won't function. I only have a few weeks experience with Orchestrator and didn't know I'd run into a problem like this so quickly -- I am trying to run the script in a "Run .Net Script" activity with the language set to "Powershell," which I believe is the recommended method.
I've tried saving the script as a file on the runbook server and then used the "Run Program" activity to run PowerShell with this file (recommended by someone during my searching) and that doesn't work either.
Is the Orchestrator service account that's running the script a member of the Exchange RBAC role groups? If not, it won't be allowed to connect to those Exchange management sessions.
The problem turned out to be related to the client's firewall and proxy settings for the service account they set up to be used by Orchestrator. They (the clients) would not grant the service account Internet access as a matter of policy.
A couple of different solutions came up: One was installing the PowerShell integration pack from CodePlex and using that -- the CodePlex PowerShell activity allowed me to explicitly set the security context of the activity, which let me get around their firewall issue by running the activity under an account which did have Internet access.
The second solution was installing the Exchange Admin integration pack and configuring a connection to the cloud host. Using the "Run Exchange PowerShell Command" activity rather than the more generic "Run .NET script" activity also allowed the code to work as expected.
Orchestrator is still x86 and the commands in your script will only run in x64.
Test this in your x86 ISE and see the same failure.
My workaround is to call the script using the "Run Program" activity within the System activities list.:
Program execution
Computer = I always start with initialize activity and then subscribe to the computer here
Program path: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
Parameters: full path to the .ps1 of your script
Working folder: c:\temp