1

Trying to figure out why 3 powershell cmdlets are not working when using AWS SM Automation inputs Script, but they do work when running them in a local script directly on my Windows instances.

The cmdlets: get-ciminstance and rename-computer work in Powershell 7, but add-computer requires importing the module Microsoft.PowerShell.Management for using the cmdlet Add-Computer in PS7, which does work in my automation document execution. However, the Import-Module CimCmdlets does not work in aws ssm automation execution, I get the error: Could not load file or assembly 'Microsoft.Management.Infrastructure.CimCmdlets, Culture=neutral, PublicKeyToken=null'. The system cannot find the file specified.The 'Get-CimInstance' command was found in the module 'CimCmdlets', but the module could not be loaded. And if importing the required module for add-computer cmdlet works, why do I get an error for this cmdlet still? Very confused.

And the error for the 2 computer cmdlets is this:

The term 'Rename-Computer' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.The term 'Add-Computer' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again`

Note that the Import-Module CimCmdlets works when running it locally when I am in the Windows instance powerhshell terminal directly.

I also noticed that the cmdlets for Rename and Add computer have the disclaimer | This cmdlet is only available on the Windows platform. Could it be that because I am using AWS SSM Automation to execute the script on my Windows instances that the "platform" is not Windows?

Just seeking any insight on this. I have read that it could be related the reference dll of Infrastructure file?

This is my systems manager automation document with the ps1 script contents inserted:

description: |-


  ---
  # Automation to domain join Windows EC2 instance

schemaVersion: '0.3'
assumeRole: 'arn:***REDACTED***:role/AutomationServiceRole'
parameters:
  secretParam:
    type: String
  hostnameParam:
    type: String
  InstanceId:
    type: 'AWS::EC2::Instance::Id'
    default: i-0983ac4bfa3d22189
mainSteps:
  - name: domainjoin
    action: 'aws:executeScript'
    inputs:
      Runtime: PowerShell 7.0
      InputPayload:
        host_name: '{{hostnameParam}}'
        secret_set: '{{secretParam}}'
      Script: |-
        # Install-Module AWSPowerShell -Force
        # Import-Module AWSPowerShell
        
        Install-Module -Name AWS.Tools.Installer -Force
        # Install-Module AWS.Tools.CloudWatchLogs -Force
        # Install-Module AWS.Tools.SecretsManager -Force
        # Import-Module AWS.Tools.Installer
        Install-Module -Name AWS.Tools.CloudWatchLogs -Force
        Install-Module -Name AWS.Tools.SecretsManager -Force
        Install-Module -Name AWS.Tools.EC2 -Force
        Install-Module -Name AWS.Tools.SecurityToken -Force
        # Install-AWSToolsModule AWS.Tools.CloudWatchLogs,AWS.Tools.SecretsManager -Force -CleanUp
        # Import-Module AWS.Tools.CloudWatchLogs
        # Import-Module AWS.Tools.SecretsManager
        
        # For using Add-Computer cmdlet
        Import-Module Microsoft.PowerShell.Management -UseWindowsPowerShell -WarningAction SilentlyContinue
        # For using Cim
        Import-Module CimCmdlets
        
        
        $inputPayload = $env:InputPayload | ConvertFrom-Json
        [string]$hostnameparam = $inputPayload.host_name
        [string]$secretparam = $inputPayload.secret_set
        
        class Logger {
            #----------------------------------------------
            [string] hidden  $cwlGroup
            [string] hidden  $cwlStream
            [string] hidden  $sequenceToken
            #----------------------------------------------
            # Log Initialization
            #----------------------------------------------
            Logger([string] $Action, [string] $HostName) {
                $this.cwlGroup = "/ps/boot/configuration/"
                $this.cwlStream = "{0}/{1}/{2}" -f $HostName, $Action,
                (Get-Date -UFormat "%Y-%m-%d_%H.%M.%S")
                $this.sequenceToken = ""
                #------------------------------------------
                if ( !(Get-CWLLogGroup -LogGroupNamePrefix $this.cwlGroup) ) {
                    New-CWLLogGroup -LogGroupName $this.cwlGroup
                    Write-CWLRetentionPolicy -LogGroupName $this.cwlGroup -RetentionInDays 3
                }
                if ( !(Get-CWLLogStream -LogGroupName $this.cwlGroup -LogStreamNamePrefix $this.cwlStream) ) {
                    New-CWLLogStream -LogGroupName $this.cwlGroup -LogStreamName $this.cwlStream
                }
            }
            #----------------------------------------
            [void] WriteLine([string] $msg) {
                $logEntry = New-Object -TypeName "Amazon.CloudWatchLogs.Model.InputLogEvent"
                #-----------------------------------------------------------
                $logEntry.Message = $msg
                $logEntry.Timestamp = (Get-Date).ToUniversalTime()
                if ("" -eq $this.sequenceToken) {
                    # First write into empty log...
                    $this.sequenceToken = Write-CWLLogEvent -LogGroupName $this.cwlGroup `
                        -LogStreamName $this.cwlStream `
                        -LogEvent $logEntry
                }
                else {
                    # Subsequent write into the log...
                    $this.sequenceToken = Write-CWLLogEvent -LogGroupName $this.cwlGroup `
                        -LogStreamName $this.cwlStream `
                        -SequenceToken $this.sequenceToken `
                        -LogEvent $logEntry
                }
            }
        }
        [Logger]$log = [Logger]::new("UserData", $hostnameparam)
        $log.WriteLine("------------------------------")
        $log.WriteLine("Log Started - V4.0")
        $RunUser = $env:username
        $log.WriteLine("PowerShell session user:" + $RunUser + " executed.")
        $log.WriteLine("Loading Secret <" + $secretparam + ">")
        $SecretObj = (Get-SECSecretValue -SecretId $secretparam -ErrorVariable SecretObjProcessError)
        if ($SecretObjProcessError) {
            $log.WriteLine($SecretObjProcessError)
            $log.WriteLine("Could not load secret <" + $secretparam + "> - terminating execution")
            return
        }
        
        [PSCustomObject]$Secret = ($SecretObj.SecretString  | ConvertFrom-Json)
        $password = $Secret.password | ConvertTo-SecureString -asPlainText -Force
        $username = $Secret.username
        $credential = New-Object System.Management.Automation.PSCredential($username, $password)
        $log.WriteLine("Domain (from Secret): <" + $Secret.domainname + ">")
        # Verify domain membership
        $compSys = Get-CimInstance -ClassName Win32_ComputerSystem | Select-Object -Property *
        #------------------------------------------------------------------------------
        if ( ($compSys.PartOfDomain) -and ($compSys.Domain -eq $Secret.domainname)) {
            $log.WriteLine("Already member of: <" + $compSys.Domain + "> - Verifying RSAT Status")
            $RSAT = (Get-WindowsFeature RSAT-AD-PowerShell)
            if ($null -eq $RSAT) {
                $log.WriteLine("<RSAT-AD-PowerShell> feature not found - terminating script")
                return
            }
            if ( (-Not $RSAT.Installed) -and ($RSAT.InstallState -eq "Available") ) {
                $log.WriteLine("Installing <RSAT-AD-PowerShell> feature")
                Install-WindowsFeature RSAT-AD-PowerShell
            }
            $comProp = (Get-ADComputer -Identity $hostnameparam -Properties Description)
            if ($null -eq $comProp.Description) {
                $log.WriteLine("Adding instance metadata to description attribute")
                $instanceId = Get-EC2InstanceMetadata -Category instanceId
                $instanceAz = Get-EC2InstanceMetadata -Category AvailabilityZone
                $instanceAccountId = (Get-STSCallerIdentity).Account
                $instanceDescription = $instanceId + "|" + $instanceAccountId + "|" + $instanceAz
                $log.WriteLine("Adding description <" + $instanceDescription + ">")
                Set-ADComputer -Identity $hostnameparam -Credential $credential -Description $instanceDescription
            }
            # Adding DNS Suffix for WFM domain
            $dnssuffixeslist = get-DnsClientGlobalSetting
            if ($Secret.domainname -in $dnssuffixeslist.SuffixSearchList) {
                $log.WriteLine("DNS suffix  <" + $Secret.domainname  + "> already exists.")
            }
            else {
                $log.WriteLine("Existing DNS suffixes <" + $dnssuffixeslist.SuffixSearchList + ">")
                $log.WriteLine("Adding DNS suffix for <" + $Secret.domainname + ">")
                $dnssuffixeslist.SuffixSearchList += $Secret.domainname
                Set-DnsClientGlobalSetting -SuffixSearchList @($dnssuffixeslist.SuffixSearchList) -ErrorVariable DnsSuffixAddError
                if ($AddCoDnsSuffixAddErrorputerProcessError) {
                    $log.WriteLine($DnsSuffixAddError)
                    $log.WriteLine("Error occured while adding DNS suffix for <" + $Secret.domainname + "> domain")
                    return
                }
                $updateddnssuffixeslist = get-DnsClientGlobalSetting
                $log.WriteLine("Updated DNS suffixes <" + $updateddnssuffixeslist.SuffixSearchList + ">")
            }
            $log.WriteLine("Terminating script - ")
            return
        }
        # Performing Domain Join
        $log.WriteLine("Performing Domain Join")
        Rename-Computer -NewName $hostnameparam -Force
        $log.WriteLine("Attempting to join domain <" + $Secret.domainname + ">")
        
        Add-Computer -ComputerName $hostnameparam -DomainName $Secret.domainname -Credential $credential -OUPath $Secret.oupath -Restart -Force -Verbose -ErrorVariable AddComputerProcessError
        if ($AddComputerProcessError) {
            $log.WriteLine($AddComputerProcessError)
            $log.WriteLine("Error occured while performing domain join operation on host - terminating execution")
            return
        }
        
        $log.WriteLine("Requesting restart...")
  • Cmdlets are found using the environmental variable PSMODULEPATH. See following for AWS : https://aws.amazon.com/blogs/compute/introducing-the-powershell-custom-runtime-for-aws-lambda/?force_isolation=true – jdweng Mar 02 '23 at 17:25
  • @jdweng Perplexed. The cmdlets that are contained within the AWS.Tools modules all work fine in my script being executed from sm automation. It is just the ones that are in .NET Core (cim, add/rename computer) that are not working. I checked the `$Env:PSModulePath` and it has this: `C:\Users\Administrator\Documents\PowerShell\Modules;C:\Program Files\PowerShell\Modules;c:\program files\powershell\7\Modules;C:\Program Files\WindowsPowerShell\Modules;C:\Windows\system32\WindowsPowerShell\v1.0\Modules;C:\Program Files (x86)\AWS Tools\PowerShell\` . What could be missing for those 3 cmdlets? – humbleStrength Mar 02 '23 at 17:49
  • And why do those 3 cmdlets work when running them in a PS session locally on the Windows instance, but don't when executing it remotely via SM automation if I am importing all that is required in my inserted script? Do I have to add something to PSModulePath explicitly? – humbleStrength Mar 02 '23 at 17:51
  • When checking Environment variables in the GUI instead of via the terminal I noticed that in the System variables, Path contained C:\Program Files\PowerShell\7\, BUT PSModulePath did not contain that. So I added C:\Program Files\PowerShell\7\ to the PSModulePath also. – humbleStrength Mar 02 '23 at 18:00
  • Still fails with aforementioned changes. I am starting to suspect it is an IAM problem with my AutomationServiceRole. I checked `net localgroup Administrators` and ssm user is not in there. – humbleStrength Mar 02 '23 at 18:11
  • Start PS by right click PS shortcut and select Run As admin. You may be running as normal user inside PS and getting a different PSMODULEPATH. You can edit the path by clicking on start and typing Edit Environmental Variables. PSModulePath is in both User and System. You are probably only getting the one in System and not User inside PS since User is probably Admin. – jdweng Mar 02 '23 at 18:51
  • You are right. The only PSModulePath Variable exists in the System variables section. I just manually added it and all the values to the User variables for Administrator section. I am going to test if it works now. – humbleStrength Mar 02 '23 at 19:43
  • No go. Same error regarding Cim instance module and cmdlets. – humbleStrength Mar 02 '23 at 20:01
  • I give up. The problem is whatever the Runtime execution environment aws ssm automation is using to run my script. Probably some integrated environment that is not a windows platform. There is nothing I can do about that. I also tested with Runtime: PowerShell Core 6.0 as that is the only other option for runtime automation in these documents. I appreciate your efforts as it were. – humbleStrength Mar 02 '23 at 20:23

0 Answers0