Aberus.AWS.Tools.EKS.KubeConfig.psm1
|
function Update-EKSKubeConfig { <# .SYNOPSIS Updates the kubeconfig file for an EKS cluster. .DESCRIPTION This function updates the kubeconfig file for an Amazon EKS cluster, allowing users to interact with the cluster using kubectl. .PARAMETER Name The name of the EKS cluster to update the kubeconfig for. .PARAMETER KubeConfigPath The path to the kubeconfig file. Relative paths are resolved against the current location and wildcard characters are not allowed. The path must not be an existing directory and its parent directory must exist. If not specified, defaults to `$HOME\.kube\config`, which is created if missing. .PARAMETER RoleArn The ARN of an IAM role to assume when authenticating against the cluster. The role is written into the generated kubeconfig, so kubectl assumes it on every call; the cluster itself is still looked up with the credentials given to this cmdlet. .PARAMETER Alias An alias for the context in the kubeconfig file. If not specified, defaults to the cluster ARN. .PARAMETER UserAlias An alias for the user in the kubeconfig file. If not specified, defaults to the cluster ARN. .PARAMETER Select Use the -Select parameter to control the cmdlet output. The default value is '*' which returns the whole result object. Specifying the name of one of its properties, 'Context' or 'Path', will result in that property being returned. Specifying -Select '^ParameterName' will result in the cmdlet returning the selected cmdlet parameter value. .PARAMETER Force This parameter overrides confirmation prompts to force the cmdlet to continue its operation. This parameter should always be used with caution. .PARAMETER ClientConfig An Amazon.EKS.AmazonEKSConfig instance used to configure the underlying service client, for example to set a custom timeout or proxy. .PARAMETER EndpointUrl The endpoint to make the call against. <b>Note:</b> This parameter is primarily for internal AWS use and is not required/should not be specified for normal usage. The cmdlets normally determine which endpoint to call based on the region specified to the -Region parameter or set as default in the shell (via Set-DefaultAWSRegion). Only specify this parameter if you must direct the call to a specific custom endpoint. .PARAMETER Region The system name of an AWS region or an AWSRegion instance. This governs the endpoint that will be used when calling service operations. Note that the AWS resources referenced in a call are usually region-specific. .PARAMETER AccessKey The AWS access key for the user account. This can be a temporary access key if the corresponding session token is supplied to the -SessionToken parameter. .PARAMETER SecretKey The AWS secret key for the user account. This can be a temporary secret key if the corresponding session token is supplied to the -SessionToken parameter. .PARAMETER SessionToken The session token if the access and secret keys are temporary session-based credentials. .PARAMETER ProfileName The user-defined name of an AWS credentials or SAML-based role profile containing credential information. The profile is expected to be found in the secure credential file shared with the AWS SDK for .NET and AWS Toolkit for Visual Studio. You can also specify the name of a profile stored in the .ini-format credential file used with the AWS CLI and other AWS SDKs. .PARAMETER ProfileLocation Used to specify the name and location of the ini-format credential file (shared with the AWS CLI and other AWS SDKs) If this optional parameter is omitted this cmdlet will search the encrypted credential file used by the AWS SDK for .NET and AWS Toolkit for Visual Studio for the 'default' and 'AWS PS Default' profiles. If the profiles are not found then the cmdlet will search in the ini-format credential file at the default location: (user's home directory)\.aws\credentials. If this parameter is specified then this cmdlet will only search the ini-format credential file at the location given. As the current folder can vary in a shell or during script execution it is advised that you use specify a fully qualified path instead of a relative path. .PARAMETER Credential An AWSCredentials object instance containing access and secret key information, and optionally a token for session-based credentials. .PARAMETER NetworkCredential Used with SAML-based authentication when ProfileName references a SAML role profile. Contains the network credentials to be supplied during authentication with the configured identity provider's endpoint. This parameter is not required if the user's default network identity can or should be used during authentication. .EXAMPLE Update-EKSKubeConfig -Name my-eks-cluster -KubeConfigPath "C:\path\to\config" -RoleArn "arn:aws:iam::123456789012:role/EKS-Role" -Alias my-cluster-alias -UserAlias my-user-alias -Region us-west-2 Updated context my-cluster-alias in C:\path\to\config Context Path ------- ---- my-cluster-alias C:\path\to\config .EXAMPLE Update-EKSKubeConfig -Region eu-west-1 -Name my-eks-cluster -ProfileName user1 Updated context arn:aws:eks:us-west-2:012345678910:cluster/example in /Users/ericn/.kube/config Context Path ------- ---- arn:aws:eks:us-west-2:012345678910:cluster/example /Users/ericn/.kube/config .LINK https://github.com/aberus/aws-eks-kubeconfig-powershell/blob/main/docs/Update-EKSKubeConfig.md .LINK Get-EKSToken #> [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] [OutputType([PSCustomObject])] param( [Parameter(Position = 0, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true, Mandatory = $true)] [string]$Name, [Parameter(ValueFromPipelineByPropertyName = $true)] [ValidateNotNullOrEmpty()] [string]$KubeConfigPath, [Parameter(ValueFromPipelineByPropertyName = $true)] [string]$RoleArn, [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $false)] [string]$Alias = $null, [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $false)] [string]$UserAlias = $null, [Parameter(ValueFromPipelineByPropertyName = $true)] [string]$Select = '*', [Parameter(ValueFromPipelineByPropertyName = $true)] [switch]$Force, [Parameter(ValueFromPipelineByPropertyName = $true)] [Amazon.EKS.AmazonEKSConfig]$ClientConfig, [Parameter(ValueFromPipelineByPropertyName = $true)] [string]$EndpointUrl, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)] [ArgumentCompleter( { param ($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) $regionHash = @{ } $regions = [Amazon.RegionEndpoint]::EnumerableAllRegions foreach ($r in $regions) { $regionHash.Add($r.SystemName, $r.DisplayName) } $regionHash.Keys | Sort-Object | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { New-Object System.Management.Automation.CompletionResult $_, $_, 'ParameterValue', $regionHash[$_] } } )] [Alias("RegionToCall")] [object]$Region, [Alias("AK")] [Parameter(ValueFromPipelineByPropertyName = $true)] [string]$AccessKey, [Alias("SK", "SecretAccessKey")] [Parameter(ValueFromPipelineByPropertyName = $true)] [string]$SecretKey, [Alias("ST")] [Parameter(ValueFromPipelineByPropertyName = $true)] [string]$SessionToken, [ArgumentCompleter( { param ($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) # Allow for new user with no profiles set up yet $profiles = Get-AWSCredentials -ListProfileDetail | Select-Object -ExpandProperty ProfileName if ($profiles) { $profiles | Sort-Object | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { New-Object System.Management.Automation.CompletionResult $_, $_, 'ParameterValue', $_ } } } )] [Parameter(ValueFromPipelineByPropertyName = $true)] [Alias("StoredCredentials", "AWSProfileName")] [string]$ProfileName, [Parameter(ValueFromPipelineByPropertyName = $true)] [Alias("AWSProfilesLocation", "ProfilesLocation")] [string]$ProfileLocation, [Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [Amazon.Runtime.AWSCredentials]$Credential, [Parameter(ValueFromPipelineByPropertyName = $true)] [System.Management.Automation.PSCredential]$NetworkCredential ) process { ValidateSelectParameter $Select @('Context', 'Path') $MyInvocation.MyCommand.Parameters if ($Force) { $ConfirmPreference = 'None' } # Validate and resolve the kubeconfig path before any service call is made. This lands in # a local rather than back in $KubeConfigPath: a parameter keeps its last bound value # when the next pipeline object does not carry the property. if ($KubeConfigPath) { $configPath = ResolveKubeConfigPath $KubeConfigPath 'KubeConfigPath' } else { $configPath = Join-Path -Path (Join-Path -Path $HOME -ChildPath '.kube') -ChildPath 'config' } if (-not $PSCmdlet.ShouldProcess($configPath, 'Update-EKSKubeConfig')) { return } # Build parameter hashtable for Get-EksCluster $eksClusterParams = @{ Name = $Name } if ($AccessKey) { $eksClusterParams.Add("AccessKey", $AccessKey) } if ($SecretKey) { $eksClusterParams.Add("SecretKey", $SecretKey) } if ($SessionToken) { $eksClusterParams.Add("SessionToken", $SessionToken) } if ($ClientConfig) { $eksClusterParams.Add("ClientConfig", $ClientConfig) } if ($Credential) { $eksClusterParams.Add("Credential", $Credential) } if ($EndpointUrl) { $eksClusterParams.Add("EndpointUrl", $EndpointUrl) } if ($NetworkCredential) { $eksClusterParams.Add("NetworkCredential", $NetworkCredential) } if ($ProfileLocation) { $eksClusterParams.Add("ProfileLocation", $ProfileLocation) } if ($ProfileName) { $eksClusterParams.Add("ProfileName", $ProfileName) } if ($Region) { $eksClusterParams.Add("Region", $Region) } # Get EKS Cluster information try { $eksCluster = Get-EksCluster @eksClusterParams } catch { Write-Error -Exception $_.Exception -Category $_.CategoryInfo.Category -TargetObject $_.TargetObject return } if ($null -eq $eksCluster) { Write-Error "EKS Cluster with name '$Name' not found." return } if (-not (Test-Path -LiteralPath $configPath -PathType Leaf)) { New-Item -ItemType File -Path $configPath -Force | Out-Null } # Load existing kubeconfig; create new if not exists if (ValidateKubeConfigFile $configPath) { $kubeConfig = ConvertFrom-Yaml (Get-Content $configPath -Raw) -Ordered foreach ($section in 'clusters', 'contexts', 'users') { if (-not $kubeConfig.$section) { Write-Verbose "The '$section' field is missing in the KubeConfig file." } # A YAML sequence of one comes back as a bare entry rather than a list, so # normalise every section to the List the Update* helpers index into. $kubeConfig.$section = AsKubeConfigList $kubeConfig.$section } if (-not $kubeConfig.'current-context') { Write-Verbose "The 'current-context' field is missing in the KubeConfig file." } } else { $kubeConfig = [ordered]@{ apiVersion = 'v1' clusters = [System.Collections.Generic.List[object]]::new() contexts = [System.Collections.Generic.List[object]]::new() 'current-context' = '' kind = 'Config' preferences = @{} users = [System.Collections.Generic.List[object]]::new() } } if ($Alias) { $contextName = $Alias } else { $contextName = $EksCluster.Arn } if ($UserAlias) { $userName = $UserAlias } else { $userName = $EksCluster.Arn } # Update or add cluster UpdateCluster -EksCluster $EksCluster -KubeConfig ([ref]$kubeConfig) # Update or add user UpdateUser -EksCluster $EksCluster -UserName $userName -KubeConfig ([ref]$kubeConfig) ` -ProfileName $ProfileName -ProfileLocation $ProfileLocation -RoleArn $RoleArn # Update or add context UpdateContext -ClusterName $EksCluster.Arn -ContexName $contextName -UserName $userName -KubeConfig ([ref]$kubeConfig) # Update the current context (optional) $kubeConfig.'current-context' = $contextName # Save updated kubeconfig $kubeConfig | ConvertTo-Yaml | Set-Content $configPath Write-Host "Updated context $contextName in $configPath" $result = [PSCustomObject]@{ Context = $contextName Path = $configPath } if ($Select.StartsWith('^')) { Get-Variable -Name $Select.Substring(1) -ValueOnly } else { SelectResponseValue $result $Select } } } function ResolveKubeConfigPath([string]$path, [string]$parameterName) { if ([System.Management.Automation.WildcardPattern]::ContainsWildcardCharacters($path)) { throw [System.Management.Automation.PSArgumentException]::new( "Wildcard characters are not allowed in $parameterName.", $parameterName) } $provider = $null $drive = $null $resolvedPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($path, [ref]$provider, [ref]$drive) if ($provider.Name -ne 'FileSystem') { throw [System.Management.Automation.PSArgumentException]::new( "$parameterName '$path' does not resolve to a file system path.", $parameterName) } if (Test-Path -LiteralPath $resolvedPath -PathType Container) { throw [System.Management.Automation.PSArgumentException]::new( "$parameterName '$resolvedPath' is a directory. Provide the path to a file.", $parameterName) } $parentPath = Split-Path -Path $resolvedPath -Parent if ($parentPath -and -not (Test-Path -LiteralPath $parentPath -PathType Container)) { throw [System.Management.Automation.PSArgumentException]::new( "The directory '$parentPath' specified in $parameterName does not exist.", $parameterName) } return $resolvedPath } function ValidateKubeConfigFile([string]$kubeConfigPath) { # Check if the kubeconfig file exists if (-not (Test-Path $kubeConfigPath)) { Write-Verbose "KubeConfig file does not exist at path: $kubeConfigPath" return $false } # Check if the kubeconfig file is not empty if ((Get-Item $kubeConfigPath).Length -eq 0) { Write-Verbose "KubeConfig file is empty: $kubeConfigPath" return $false } try { # Attempt to parse the kubeconfig file as YAML Get-Content $kubeConfigPath -Raw | ConvertFrom-Yaml | Out-Null } catch { Write-Verbose "Failed to parse KubeConfig file as YAML: $_" return $false } # If all checks pass return $true } function ValidateSelectParameter([string]$Select, [string[]]$SelectableProperty, [System.Collections.IDictionary]$CommandParameter) { # Mirrors the -Select validation of the generated AWS.Tools cmdlets: '*' for the whole # result, one of the result's properties, or '^Name' for a parameter the caller passed. if ($Select -eq '*') { return } if ($Select.StartsWith('^')) { $parameterName = $Select.Substring(1) $commonParameter = [System.Management.Automation.PSCmdlet]::CommonParameters + [System.Management.Automation.PSCmdlet]::OptionalCommonParameters if ($CommandParameter.Keys -contains $parameterName -and $commonParameter -notcontains $parameterName) { return } } elseif ($SelectableProperty -contains $Select) { return } throw [System.ArgumentException]::new('Invalid value for -Select parameter.', 'Select') } function SelectResponseValue([object]$Response, [string]$Select) { if ($Select -eq '*') { return $Response } # -Select takes a property path, so 'Status.Token' walks into the nested member. $value = $Response foreach ($segment in $Select.Split('.')) { if ($null -eq $value) { break } $value = $value.$segment } return $value } function QuoteExecArgument([string]$Value) { # The exec entry packs every argument into a single -command string, so values that # contain spaces - a -ProfileLocation under 'C:\Users\Jane Doe' for instance - have # to survive being re-parsed by pwsh. return "'" + $Value.Replace("'", "''") + "'" } function ResolveAWSCredential([string]$AccessKey, [string]$SecretKey, [string]$SessionToken, [string]$ProfileName, [string]$ProfileLocation, [string]$RoleArn, [Amazon.Runtime.AWSCredentials]$Credential, [System.Management.Automation.PSCredential]$NetworkCredential) { # Same precedence the AWS.Tools cmdlets use: an explicit AWSCredentials object, then # static keys, then a named profile, then whatever the shell or the SDK chain resolves. if ($Credential) { $credentials = $Credential } elseif ($AccessKey -or $SecretKey) { if (-not $AccessKey -or -not $SecretKey) { throw [System.Management.Automation.PSArgumentException]::new( 'Both -AccessKey and -SecretKey are needed to use static credentials.', 'AccessKey') } if ($SessionToken) { $credentials = [Amazon.Runtime.SessionAWSCredentials]::new($AccessKey, $SecretKey, $SessionToken) } else { $credentials = [Amazon.Runtime.BasicAWSCredentials]::new($AccessKey, $SecretKey) } } else { $lookup = @{ } if ($ProfileName) { $lookup['ProfileName'] = $ProfileName if ($ProfileLocation) { $lookup['ProfileLocation'] = $ProfileLocation } } # Get-AWSCredential only knows about profiles and the shell defaults, so fall back # to the SDK's own chain (environment variables, container and instance metadata). $credentials = Get-AWSCredential @lookup if (-not $credentials -and -not $ProfileName) { $credentials = [Amazon.Runtime.FallbackCredentialsFactory]::GetCredentials() } } if ($NetworkCredential -and $credentials -is [Amazon.Runtime.FederatedAWSCredentials]) { # A SAML role profile prompts for the identity provider password unless the caller # supplies one, which is what -NetworkCredential is for. $networkIdentity = $NetworkCredential.GetNetworkCredential() $callback = { param($callbackArgs) $networkIdentity }.GetNewClosure() $credentials.Options.CredentialRequestCallback = [Func[Amazon.Runtime.CredentialRequestCallbackArgs, System.Net.NetworkCredential]]$callback } if ($RoleArn -and $credentials) { # Same session name the AWS CLI uses for 'aws eks get-token --role-arn', so the # calls are recognisable in CloudTrail. Nothing is called until the credentials are # actually used, which keeps -RoleArn free until the token is signed. $credentials = [Amazon.Runtime.AssumeRoleAWSCredentials]::new($credentials, $RoleArn, 'EKSGetTokenAuth') } return $credentials } function AsKubeConfigList([object]$Value) { # ::new() rather than New-Object: on PowerShell 7.6.3 a List[object] built by New-Object # makes @() throw 'Argument types do not match', which any caller wrapping a section in # @() would hit. $list = [System.Collections.Generic.List[object]]::new() foreach ($item in @($Value)) { if ($null -ne $item) { $list.Add($item) } } return , $list } function UpdateCluster([PSCustomObject]$EksCluster, [ref]$KubeConfig) { # Extract cluster details $clusterName = $EksCluster.Arn $endpoint = $EksCluster.Endpoint $certificateAuthorityData = $EksCluster.CertificateAuthority.Data # Check if the cluster already exists in kubeconfig $clusterIndex = $KubeConfig.Value.clusters.FindIndex({ $args.name -eq $clusterName }) $clusterEntry = [ordered]@{ cluster = [ordered]@{ 'certificate-authority-data' = $certificateAuthorityData server = $endpoint } name = $clusterName } if ($clusterIndex -ge 0) { # Update existing cluster entry $KubeConfig.Value.clusters[$clusterIndex] = $clusterEntry } else { # Add new cluster entry. += would rebuild the List as an Object[], which has no # FindIndex, so the next call against the same kubeconfig would throw. $KubeConfig.Value.clusters.Add($clusterEntry) } } function UpdateUser([PSCustomObject]$EksCluster, [ref]$KubeConfig, [string]$ProfileName, [string]$ProfileLocation, [string]$RoleArn, [string]$UserName) { $outpostConfig = $EksCluster.OutpostConfig $region = $EksCluster.Arn.Split(":")[3] if ($outpostConfig) { #$clusterIdentificationParameter = "--cluster-id" $clusterIdentificationValue = $EksCluster.Id } else { #$clusterIdentificationParameter = "--cluster-name" $clusterIdentificationValue = $EksCluster.Name } # Only the parameters that carry a value are written out: an empty one would leave # kubectl running 'Get-EKSToken -ProfileName -Region eu-west-1', which fails to bind. # Static credentials are deliberately never written into a kubeconfig file. $tokenArguments = [System.Collections.Generic.List[string]]::new() $tokenArguments.Add("-ClusterNameOrId $(QuoteExecArgument $clusterIdentificationValue)") if ($RoleArn) { $tokenArguments.Add("-RoleArn $(QuoteExecArgument $RoleArn)") } if ($ProfileName) { $tokenArguments.Add("-ProfileName $(QuoteExecArgument $ProfileName)") } if ($ProfileLocation) { $tokenArguments.Add("-ProfileLocation $(QuoteExecArgument $ProfileLocation)") } $tokenArguments.Add("-Region $(QuoteExecArgument $region)") # Check if user entry exists and update if necessary $userIndex = $KubeConfig.Value.users.FindIndex({ $args.name -eq $UserName }) # Add new user entry $userEntry = [ordered]@{ name = $userName user = [ordered]@{ exec = [ordered]@{ apiVersion = 'client.authentication.k8s.io/v1' args = @( '-command', "&{ &'Get-EKSToken' $($tokenArguments -join ' ')}" ) command = 'pwsh' interactiveMode = 'IfAvailable' provideClusterInfo = $False } } } if ($ProfileName) { $userEntry.user.exec.env = @( [ordered]@{ name = 'AWS_PROFILE' value = $ProfileName } ) } if ($userIndex -ge 0) { $KubeConfig.Value.users[$userIndex] = $userEntry } else { # Add new user entry $KubeConfig.Value.users.Add($userEntry) } } function UpdateContext([string]$ClusterName, [string]$ContexName, [ref]$KubeConfig, [string]$UserName) { # Check if context entry exists and update if necessary $contextIndex = $KubeConfig.Value.contexts.FindIndex({ $args.name -eq $ContexName }) $contextEntry = [ordered]@{ context = [ordered]@{ cluster = $ClusterName user = $UserName } name = $ContexName } if ($contextIndex -ge 0) { # Update context configuration if needed $KubeConfig.Value.contexts[$contextIndex] = $contextEntry } else { # Add new context entry $KubeConfig.Value.contexts.Add($contextEntry) } } function Get-EKSToken { <# .SYNOPSIS Gets a bearer token for authenticating against an EKS cluster. .DESCRIPTION This function returns the ExecCredential document kubectl expects from an exec credential plugin. The token is a presigned STS GetCallerIdentity URL bound to the cluster, and is the call Update-EKSKubeConfig writes into the user entry of the kubeconfig file. .PARAMETER ClusterNameOrId The name of the EKS cluster, or its id when the cluster runs on an Outpost. .PARAMETER RoleArn The ARN of an IAM role to assume before the token is signed. The credentials resolved from the other parameters are used to assume it, and the token then authenticates as the role. .PARAMETER Select Use the -Select parameter to control the cmdlet output. The default value is '*' which returns the whole ExecCredential document as the JSON kubectl expects. Specifying the name of one of its properties, such as 'Status.Token' or 'Status.ExpirationTimestamp', will result in that property being returned. Specifying -Select '^ParameterName' will result in the cmdlet returning the selected cmdlet parameter value. .PARAMETER ClientConfig An Amazon.SecurityToken.AmazonSecurityTokenServiceConfig instance used to configure the underlying service client, for example to set a custom timeout or proxy. The token is signed for the resolved region regardless of the region carried by the configuration. .PARAMETER EndpointUrl The endpoint to make the call against. <b>Note:</b> This parameter is primarily for internal AWS use and is not required/should not be specified for normal usage. The cmdlets normally determine which endpoint to call based on the region specified to the -Region parameter or set as default in the shell (via Set-DefaultAWSRegion). Only specify this parameter if you must direct the call to a specific custom endpoint. .PARAMETER Region The system name of an AWS region or an AWSRegion instance. This governs the endpoint that will be used when calling service operations. Note that the AWS resources referenced in a call are usually region-specific. .PARAMETER AccessKey The AWS access key for the user account. This can be a temporary access key if the corresponding session token is supplied to the -SessionToken parameter. .PARAMETER SecretKey The AWS secret key for the user account. This can be a temporary secret key if the corresponding session token is supplied to the -SessionToken parameter. .PARAMETER SessionToken The session token if the access and secret keys are temporary session-based credentials. .PARAMETER ProfileName The user-defined name of an AWS credentials or SAML-based role profile containing credential information. The profile is expected to be found in the secure credential file shared with the AWS SDK for .NET and AWS Toolkit for Visual Studio. You can also specify the name of a profile stored in the .ini-format credential file used with the AWS CLI and other AWS SDKs. .PARAMETER ProfileLocation Used to specify the name and location of the ini-format credential file (shared with the AWS CLI and other AWS SDKs) If this optional parameter is omitted this cmdlet will search the encrypted credential file used by the AWS SDK for .NET and AWS Toolkit for Visual Studio for the 'default' and 'AWS PS Default' profiles. If the profiles are not found then the cmdlet will search in the ini-format credential file at the default location: (user's home directory)\.aws\credentials. If this parameter is specified then this cmdlet will only search the ini-format credential file at the location given. As the current folder can vary in a shell or during script execution it is advised that you use specify a fully qualified path instead of a relative path. .PARAMETER Credential An AWSCredentials object instance containing access and secret key information, and optionally a token for session-based credentials. .PARAMETER NetworkCredential Used with SAML-based authentication when ProfileName references a SAML role profile. Contains the network credentials to be supplied during authentication with the configured identity provider's endpoint. This parameter is not required if the user's default network identity can or should be used during authentication. .EXAMPLE Get-EKSToken -ClusterNameOrId my-eks-cluster -Region us-west-2 { "kind": "ExecCredential", "apiVersion": "client.authentication.k8s.io/v1", "spec": {}, "status": { "expirationTimestamp": "2024-08-22T12:09:49Z", "token": "k8s-aws-v1.aHR0cHM6Ly9zdHMudXMtd2VzdC0yLmFtYXpvbmF3cy5jb20v..." } } .EXAMPLE Get-EKSToken -ClusterNameOrId my-eks-cluster -Region us-west-2 -ProfileName user1 -Select Status.Token k8s-aws-v1.aHR0cHM6Ly9zdHMudXMtd2VzdC0yLmFtYXpvbmF3cy5jb20v... .LINK https://github.com/aberus/aws-eks-kubeconfig-powershell/blob/main/docs/Get-EKSToken.md .LINK Update-EKSKubeConfig #> [CmdletBinding()] [OutputType([string])] param( [Parameter(Position = 0, Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [Alias("ClusterName", "ClusterId")] [string]$ClusterNameOrId, [Parameter(ValueFromPipelineByPropertyName = $true)] [string]$RoleArn, [Parameter(ValueFromPipelineByPropertyName = $true)] [string]$Select = '*', [Parameter(ValueFromPipelineByPropertyName = $true)] [Amazon.SecurityToken.AmazonSecurityTokenServiceConfig]$ClientConfig, [Parameter(ValueFromPipelineByPropertyName = $true)] [string]$EndpointUrl, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)] [ArgumentCompleter( { param ($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) $regionHash = @{ } $regions = [Amazon.RegionEndpoint]::EnumerableAllRegions foreach ($r in $regions) { $regionHash.Add($r.SystemName, $r.DisplayName) } $regionHash.Keys | Sort-Object | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { New-Object System.Management.Automation.CompletionResult $_, $_, 'ParameterValue', $regionHash[$_] } } )] [Alias("RegionToCall")] [object]$Region, [Alias("AK")] [Parameter(ValueFromPipelineByPropertyName = $true)] [string]$AccessKey, [Alias("SK", "SecretAccessKey")] [Parameter(ValueFromPipelineByPropertyName = $true)] [string]$SecretKey, [Alias("ST")] [Parameter(ValueFromPipelineByPropertyName = $true)] [string]$SessionToken, [ArgumentCompleter( { param ($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) # Allow for new user with no profiles set up yet $profiles = Get-AWSCredentials -ListProfileDetail | Select-Object -ExpandProperty ProfileName if ($profiles) { $profiles | Sort-Object | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { New-Object System.Management.Automation.CompletionResult $_, $_, 'ParameterValue', $_ } } } )] [Parameter(ValueFromPipelineByPropertyName = $true)] [Alias("StoredCredentials", "AWSProfileName")] [string]$ProfileName, [Parameter(ValueFromPipelineByPropertyName = $true)] [Alias("AWSProfilesLocation", "ProfilesLocation")] [string]$ProfileLocation, [Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [Amazon.Runtime.AWSCredentials]$Credential, [Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [System.Management.Automation.PSCredential]$NetworkCredential ) ValidateSelectParameter $Select @('Kind', 'ApiVersion', 'Spec', 'Status', 'Status.Token', 'Status.ExpirationTimestamp') ` $MyInvocation.MyCommand.Parameters $regionArgs = New-Object Amazon.PowerShell.Common.StandaloneRegionArguments $regionArgs.Region = $Region $regionArgs.ProfileLocation = $ProfileLocation $regionEndpoint = [Amazon.PowerShell.Common.IAWSRegionArgumentsMethods]::GetRegion($regionArgs, $true, $ExecutionContext.SessionState) if (-not $regionEndpoint) { Write-Error -Message "No region specified or obtained from persisted/shell defaults." -Category InvalidOperation return } try { $credentials = ResolveAWSCredential -AccessKey $AccessKey -SecretKey $SecretKey -SessionToken $SessionToken ` -ProfileName $ProfileName -ProfileLocation $ProfileLocation -RoleArn $RoleArn ` -Credential $Credential -NetworkCredential $NetworkCredential } catch { Write-Error -Exception $_.Exception -Category $_.CategoryInfo.Category -TargetObject $_.TargetObject return } if (-not $credentials) { Write-Error -Message "No credentials specified or obtained from persisted/shell defaults." -Category InvalidOperation return } # Create the STS client configuration if ($ClientConfig) { $config = $ClientConfig } else { $config = New-Object Amazon.SecurityToken.AmazonSecurityTokenServiceConfig } # RegionEndpoint and ServiceURL clear each other on a client configuration, so hold on # to the region the request has to be signed for and only touch the one that wins: # -EndpointUrl first, then a ServiceURL the caller already put on -ClientConfig. $signingRegion = $regionEndpoint.SystemName if ($EndpointUrl) { $config.ServiceURL = $EndpointUrl } elseif (-not $config.ServiceURL) { $config.RegionEndpoint = $regionEndpoint } # Create the GetCallerIdentity request $getCallerIdentityRequest = [Amazon.SecurityToken.Model.GetCallerIdentityRequest]::new() $marshaller = [Amazon.SecurityToken.Model.Internal.MarshallTransformations.GetCallerIdentityRequestMarshaller]::new() $request = $marshaller.Marshall($getCallerIdentityRequest) # $request = [Amazon.Runtime.Internal.DefaultRequest]::new($getCallerIdentityRequest, $config.AuthenticationServiceName) # $request.Parameters.Add("Action", "GetCallerIdentity") # $request.Parameters.Add("Version", "2011-06-15") $request.UseQueryString = $true $request.HttpMethod = "GET" $request.Endpoint = [Uri]::new($config.DetermineServiceOperationEndpoint($getCallerIdentityRequest).URL) $expirationTime = New-TimeSpan -Seconds 60 $request.Parameters["X-Amz-Expires"] = [int]$expirationTime.TotalSeconds.ToString([System.Globalization.CultureInfo]::InvariantCulture) # Get credentials (assuming credentials is defined) $immutableCredentials = $credentials.GetCredentials() if ($immutableCredentials.UseToken) { $request.Parameters["X-Amz-Security-Token"] = $immutableCredentials.Token } $request.Headers["x-k8s-aws-id"] = $ClusterNameOrId # Sign the request $signingResult = [Amazon.Runtime.Internal.Auth.AWS4PreSignedUrlSigner]::SignRequest( $request, $config, [Amazon.Runtime.Internal.Util.RequestMetrics]::new(), $immutableCredentials.AccessKey, $immutableCredentials.SecretKey, $config.AuthenticationServiceName, $signingRegion ) # Calculate token expiration $tokenExpiration = $signingResult.DateTime.AddMinutes(14) # Compose the URL $authorization = "&" + $signingResult.ForQueryParameters $url = [Amazon.Runtime.AmazonServiceClient]::ComposeUrl($request).AbsoluteUri + $authorization $bytes = [System.Text.Encoding]::UTF8.GetBytes($url) $encodedText = [Convert]::ToBase64String($bytes) $expirationTimestamp = [DateTime]::new($tokenExpiration.Year, $tokenExpiration.Month, $tokenExpiration.Day, $tokenExpiration.Hour, $tokenExpiration.Minute, $tokenExpiration.Second, $tokenExpiration.Kind) $execCredential = [PSCustomObject]@{ kind = 'ExecCredential' apiVersion = 'client.authentication.k8s.io/v1' spec = @{} status = @{ expirationTimestamp = $expirationTimestamp # "2024-08-22T12:09:49Z" token = 'k8s-aws-v1.' + $encodedText.Replace("=", "") } } if ($Select -eq '*') { ConvertTo-Json $execCredential } elseif ($Select.StartsWith('^')) { Get-Variable -Name $Select.Substring(1) -ValueOnly } else { SelectResponseValue $execCredential $Select } } |