How do I set multiple AppSettings values using xWebConfigKeyValue? - powershell

I'm trying to use the xWebConfigKeyValue resource of the DSC module xWebAdministration to set multiple values on our application's web.config file. This is an excerpt from our configuration with the main activities surrounding the web.config changes:
Configuration C4M
{
Param(
[Parameter(Mandatory)]
[string] $BuildDropLocation
)
Import-DscResource -Module xWebAdministration
Node $AllNodes.NodeName
{
$managementPortalInstallPath = 'c:\Company\ManagementPortal'
File ManagementPortalContents
{
DestinationPath = $managementPortalInstallPath
SourcePath = "$BuildDropLocation\ManagementPortal"
Type = 'Directory'
Recurse = $True
}
xWebConfigKeyValue RecaptchaPublicKey
{
WebsitePath = $managementPortalInstallPath
ConfigSection = 'AppSettings'
Key = 'recaptchaPublicKey'
Value = $Node.RecaptchaPublicKey
DependsOn = '[File]ManagementPortalContents'
}
xWebConfigKeyValue RecaptchaPrivateKey
{
WebsitePath = $managementPortalInstallPath
ConfigSection = 'AppSettings'
Key = 'recaptchaPrivateKey'
Value = $Node.RecaptchaPrivateKey
DependsOn = '[File]ManagementPortalContents'
}
}
}
But when I try to run the configuration, I get the following error:
Add-NodeKeys : The key properties combination
'C:\Company\ManagementPortal::AppSettings' is duplicated for keys
'WebsitePath,ConfigSection' of resource 'xWebConfigKeyValue' in node
'myNode'. Please make sure key properties are unique for each
resource in a node. At line:160 char:9
+ Add-NodeKeys $keyValues $keyNames $keywordName
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [Write-Error], InvalidOperationException
+ FullyQualifiedErrorId : DuplicateKeyInNode,Add-NodeKeys
After seeing the error, I took a look at the modules schema.mof file, and noticed that the appSettings key is not a key to the configuration at C:\Program Files\WindowsPowerShell\Modules\xWebAdministration\DSCResources\MSFT_xWebConfigKeyValue\MSFT_xWebConfigKeyValue.schema.mof:
[ClassVersion("1.0.0.0"), FriendlyName("xWebConfigKeyValue")]
class MSFT_xWebConfigKeyValue : OMI_BaseResource
{
[Key, Description("Path to website location(IIS or WebAdministration format)")] String WebsitePath;
[Key, Description("Config Section to be update"), ValueMap{"AppSettings"}, Values{"AppSettings"}] String ConfigSection;
[Write, ValueMap{"Present","Absent"}, Values{"Present","Absent"}] String Ensure;
[Required, Description("Key for AppSettings")] String Key;
[Write, Description("Value for AppSettings")] String Value;
[Write, Description("If the given key value pair is for attribute, default is element")] Boolean IsAttribute;
};
Since only the site path and config section are keys, I can't have multiple instances of the resource pointing to the same appSettings block but with different appSetting keys. How can I configure multiple appSetting keys then?

You can't configure two xWebConfigKeyValue resources in the same Configuration because, as you've found, the resource key only contains the WebsitePath and ConfigSection properties and doesn't discriminate on the Key property.
I think your immediate options are:
Create a new cWebConfigKeyValue resource module based on xWebConfigKeyValue, and add the "Key" attribute to the "Key" property in the "cWebConfigKeyValue.schema.mof" to fix it yourself.
or
Define the clashing resources in separate Configuration blocks. I've had to do this in a project that uses the xService resource.
Neither of these is a particularly good solution, but it might unblock you until a better fix comes along.

Related

ExtendedPropertyDefinition declaration for EmailMessage in Powershell throws exception

since I got to know that ExtendedProperties have its limit for a specific mailbox in the EWS cloud I am trying to switch up my code to have only one ExtendedProperty and just change its value each time I am assigning the property to an e-mail message I am sending to then find it and work on the e-mail message object later on in the program.
I am having a hard time setting this up correctly even though I am following the docs, but it just seems to not work out for me.
This is the code part that throws an Exception: "Multiple ambigious overloads found for "ExtendedPropertyDefinition" and the argument count "3" :
# email declaration exposing the $email object
.
.
.
# property declaration and setting the value
# since I want to have only one extended property, this is actually a valid GUID string that I then # convert to a Guid type
$GUIDproperty = "00000000-0000-0000-0000-000000000000"
$propertyGUID = [Guid]$GUIDproperty
# since I want to have a unique value each time set to the existing extended property
$propertyValue = [guid]::NewGuid().ToString()
$propertyName = "Id"
$ExtendedProperty = [Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition]::new($propertyGUID, $propertyName, $propertyType)
# well I dont even reach this part, but just for the big picture
$email.SetExtendedProperty($ExtendedProperty, $propertyValue)
The docs I have followed for that are the following:
https://learn.microsoft.com/en-us/dotnet/api/microsoft.exchange.webservices.data.extendedpropertydefinition.-ctor?view=exchange-ews-api#microsoft-exchange-webservices-data-extendedpropertydefinition-ctor(microsoft-exchange-webservices-data-defaultextendedpropertyset-system-string-microsoft-exchange-webservices-data-mapipropertytype)
https://learn.microsoft.com/en-us/dotnet/api/microsoft.exchange.webservices.data.folder.setextendedproperty?redirectedfrom=MSDN&view=exchange-ews-api#Microsoft_Exchange_WebServices_Data_Folder_SetExtendedProperty_Microsoft_Exchange_WebServices_Data_ExtendedPropertyDefinition_System_Object_
https://learn.microsoft.com/en-us/dotnet/api/system.guid?view=net-7.0
The following works okay for me
$propertyType = [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String
$GUIDproperty = "82e3d64f-e26d-4321-8fc3-c31aa790197c"
$propertyGUID = [Guid]$GUIDproperty
$propertyValue = [guid]::NewGuid().ToString()
$propertyName = "MyPropId"
$ExtendedProperty = [Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition]::new($propertyGUID, $propertyName, $propertyType)
return $ExtendedProperty
You don't specify what you using in the $propertyType so that maybe it, could also be to do with the versions you using. What version of PowerShell and the EWS Managed API are you trying ?

Pulumi Azure: get keyvault key version

I want to use customer managed key for the sql server. For that I need to load the key from the keyvault but I cannot get the version of the key, which is required according to the pulumi documentation:
:param pulumi.Input[str] key_name: The name of the server key to be
operated on (updated or created). The key name is required to be in
the format of 'vault_key_version'. For example, if the keyId is
https://YourVaultName.vault.azure.net/keys/YourKeyName/YourKeyVersion,
then the server key name should be formatted as:
YourVaultName_YourKeyName_YourKeyVersion
Relevant code:
def create_tde_key(self, key: keyvault.Key, vault_name: str):
key_name = "{}_{}_{}".format(vault_name, key.name, key.version ) // here I need the version but there is only key.key_uri_with_version
tde_key = sql.ServerKey('tde-key-' + self.location_flag,
key_name = key_name,
resource_group_name = self.resource_group_name,
server_name = self.sql_server,
server_key_type = sql.ServerKeyType.AZURE_KEY_VAULT,
uri = key.key_uri_with_version
)
tried to disassemble key.key_uri_with_version this way:
key_version = key.key_uri_with_version[key.key_uri_with_version.rfind('/') + 1:]
but then I got TypeError: 'Output' object is not callable
I tried to use lambdas to get the string Output from key.key_uri_with_version but got the same TypeError as before.
I tried hardcoding the version and it is working so somehow I need to get the keyversion.
Any idea what should I do?

Why is PowerShell DSC saying I have a duplicate resource identifier when they are in different nodes?

I'm trying to do something relatively simple in PowerShell DSC. I want to ensure that the same file is on two servers:
configuration.ps1:
Configuration MyConfig {
# I want this block to be common to both nodes
Node $AllNodes {
File DirectoryCopy {
Ensure = "Present"
Type = "File"
Recurse = $true
SourcePath = ".\example.txt"
DestinationPath = "%userprofile%\example.txt"
PsDscRunAsCredential = Get-Credential
}
}
}
Agents -ConfigurationData .\data.psd1
data.psd1:
#{
AllNodes = #(
#{
NodeName = "server1"
Role = "ExampleRole1NotUsedYet"
},
#{
NodeName = "server2"
Role = "ExampleRole2NotUsedYet"
}
)
}
This doesn't work, and produces errors:
PSDesiredStateConfiguration\File : A duplicate resource identifier
'[File]DirectoryCopy' was found while processing the specification for
node 'System.Collections.Hashtable'. Change the name of this resource
so that it is unique within the node specification.
I think there's some basic concept about PowerShell DSC that I'm missing out on. Is there a way for me to apply this file to both nodes? Ideally, I'd like to apply some resources globally, and then apply some to just dev/prod systems.
$AllNodes is an array containing [hashtable]s, so when you use it directly, it's being enumerated, and then each element (a [hashtable]) when referenced as a node name is being converted to a string, which is to just going to display the class name; that's why the error says your node is called System.Collections.Hashtable instead of a name you expect.
Since both hashtables will end up being the same string (regardless of their contents), you are trying to create 2 File resources with the same name for the same node, which won't work.
What you want is to reference the elements of each hashtable, in this case the NodeName:
Configuration MyConfig {
# I want this block to be common to both nodes
Node $AllNodes.NodeName {
File DirectoryCopy {
Ensure = "Present"
Type = "File"
Recurse = $true
SourcePath = ".\example.txt"
DestinationPath = "%userprofile%\example.txt"
PsDscRunAsCredential = Get-Credential
}
}
}

Dynamically pass parameters to DSC resource?

I was working on a composite resource when I came across the issue of being able to dynamically pass parameters to a DSC resource and wondered if there's another way to tackle this that I'm missing.
Basically, I have the an array (Below) that contains one PSObject per desired file-share. Each of these PSObjects has properties that are used as parameters for my DSC resource (In this case the cLocalFileShare community resource).
The issue is, not all of these objects have all of the parameters defined. For example, some of my shares don't have any users/groups assigned to the ReadAccess permission, but in my ForEach loop (Below), a $null value is being passed to the actual resource as this permissions isn't defined, and this causes the resource to error as it is trying to set ReadAccess permissions to user $null.
My issue is, how do I tackle this - for this resource and others?
I've tried splatting the parameters in the DSC resource, but this doesn't seem to be supported. If this worked, I could build a different parameter list and pass that.
Somebody on Reddit suggested passing a string that contained all of the parameters, but again this doesn't seem to be supported.
My worst fear is, I will have to edit each resource to support (and ultimately ignore) $null values which seems like a really bad way to tackle this.
So, here's my array containing a PSObject per file share.
$MyConfig = #(
#{
Path = 'D:\Shares\Accounting'
Name = 'Accounting'
Ensure = 'Present'
ChangeAccess = 'AccountingAdmins'
ReadAccess = 'AccountingInterns,FinanceDepartment'
}
#{
Path = 'D:\Shares\Software'
Name = 'Software$'
Ensure = 'Present'
ReadAccess = 'DomainUsers'
}
)
Now, within the actual DSC configuration...
configuration {
ForEach ($ShareProperties in $MyConfig) {
# Each resource is named after the Path specified, but with the colon replaced as that's not valid character for the resource name
cLocalFileShare $ShareProperties.Path.Replace(':','__') {
Path = $ShareProperties.Path
Name = $ShareProperties.Name
Ensure = $ShareProperties.Ensure
ChangeAccess = $ShareProperties.ChangeAccess
ReadAccess = $ShareProperties.ReadAccess
}
}
}
Not the most elegant solution. But you could use a conditional section within your ForEach loop.
configuration {
ForEach ($ShareProperties in $MyConfig) {
# Each resource is named after the Path specified, but with the colon replaced as that's not valid character for the resource name
if ($ShareProperties.ChangeAccess -eq $null) {
cLocalFileShare $ShareProperties.Path.Replace(':','__') {
Path = $ShareProperties.Path
Name = $ShareProperties.Name
Ensure = $ShareProperties.Ensure
ReadAccess = $ShareProperties.ReadAccess
}
}
else
{
cLocalFileShare $ShareProperties.Path.Replace(':','__') {
Path = $ShareProperties.Path
Name = $ShareProperties.Name
Ensure = $ShareProperties.Ensure
ChangeAccess = $ShareProperties.ChangeAccess
ReadAccess = $ShareProperties.ReadAccess
}
}
}
}

ExtractAssociatedIcon gives exception in network share using powershell

I am not able to load Icon when accessing from a network share drive in powershell
$IconPath = $pwd.Path + "\Icons\InstallIcon-F.ico"
$HFForm.icon = [System.Drawing.Icon]::ExtractAssociatedIcon($IconPath)
I am getting this error:
Exception calling "ExtractAssociatedIcon" with "1" argument(s): "The given path's format is not supported."
$HFForm.icon = [System.Drawing.Icon]::ExtractAssociatedIcon <<<< ($IconPath)
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException
My testing shows the same as PeterK's. If I use a drive letter its fine, but an unmapped network share is not.
I was able to make it work by mapping the network share to a drive letter. So:
$something = [system.drawing.icon]::extractassociatedicon("c:\windows\system32\notepad.exe")
caused no errors. Neither did:
$something = [system.drawing.icon]::extractassociatedicon(($test.fullname))
with $test.fullname just being a mapped network file path.
It is a good idea to expand out your variables too so we can see what you're actually passing in. Because if I browse to a network file share and expand $pwd.path:
Microsoft.PowerShell.Core\FileSystem::\\user-pc\users
You are almost certainly passing that in. So have a look. I haven't done much with how Powershell formats its display so I'm sure you can find the 'tty' equiv settings, but just in the meantime do this:
$IconPath = $pwd.Path.split('::')[2] + "\Icons\InstallIcon-F.ico"
Convert your icon to a base64 representation of it's binary data, then store it inside the script itself (as text).
Once it's encoded as base64, you can use this command to convert it back into an icon, bypassing the UNC path issue.
$HFForm.icon = [System.Convert]::FromBase64String('
AAABAAkAAAAAAAEAIABbfQEAlgAAAICAAAABACAAKAgBAPF9AQBgYAAAAQAgAKiUAAAZhgIASEgA
#
# There will be hundreds rows depending on your icon's size.
#
AMADAADAAwAA4AcAAPAPAADwDwAA+A8AAPgPAAD4DwAA/B8AAPwfAAA=')
BASE64 snippet.
#Be sure to edit the path to icon.
#
#Hint the result is copied to your clipboard - clip = clip.exe == google it.
$path = "A:\R2-D2-32x32.ico"
[convert]::ToBase64String((get-content $path -encoding byte)) | Clip
#After running the clip command, right click paste between the quotes
$HFForm.icon = [System.Convert]::FromBase64String('')
i have a solution for it.
At first you have to import the SHGetFileInfo Methode and create the structure SHFILEINFO.
$code = #"
using System;
using System.Drawing;
using System.Runtime.InteropServices;
namespace System
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct SHFILEINFO
{
public IntPtr hIcon;
public int iIcon;
public uint dwAttributes;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string szDisplayName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
public string szTypeName;
};
public class SHGETFILEINFO
{
[DllImport("shell32.dll", CharSet = CharSet.Unicode)]
public static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes,ref SHFILEINFO psfi, uint cbSizeFileInfo, uint uFlags);
}
}
"#
Add-Type -TypeDefinition $code
Creates the structure object.
#Path to the exe.
$Path = \\test.de\tes
[System.SHFILEINFO]$FileinfoStruct = New-Object System.SHFILEINFO
Gets the size of the structure
$Size = [System.Runtime.InteropServices.Marshal]::SizeOf($FileinfoStruct)
Gets fills the structure Variable with the File infos.
[System.SHGETFILEINFO]::SHGetFileInfo($Path,0, [ref]$FileinfoStruct,$Size,0x000000100)
Creates the icon.
$ICON = [System.Drawing.Icon]::FromHandle($FileinfoStruct.hIcon)