Repository: hashicorp/vault
File: builtin/logical/aws/client.go (Go)

Code before:
		// explicitly disable environment and shared credential providers when using Web Identity Token Fetcher
		// enables WIF usage in environments that may use AWS Profiles or environment variables for other use-cases
		opts = append(opts, awsutil.WithEnvironmentCredentials(false), awsutil.WithSharedCredentials(false))
	}

	// at this point, in the IAM case, regions contains config.Region, if it was set.
	//                                 endpoints contains iam_endpoint, if it was set.
	// in the sts case, regions contains sts_region, if it was set, then the sts_fallback_regions in order, if they were set.
	//                  endpoints contains sts_endpint, if it wa set, then sts_fallback_endpoints in order, if they were set.

	logger.Info("pre", "region", fmt.Sprintf("%+v", regions), "endpoint", fmt.Sprintf("%+v", endpoints))

	// case in which nothing was supplied
	if len(regions) == 0 {
		// fallback region is in descending order, AWS_REGION, or AWS_DEFAULT_REGION, or us-east-1
		regions = append(regions, fallbackRegion)
	}

	if len(endpoints) == 0 {
		switch clientType {
		case "sts":
			for _, v := range regions {
				endpoints = append(endpoints, matchingSTSEndpoint(v))
			}
		case "iam":
			for range regions {
				// for custom IAM implementations, iam_endpoint would have been set above - this case will only occur in
				// real IAM deployments, where you don't specify an endpoint, and in fact, AWS doesn't seem to like it
				// if you do.
				endpoints = append(endpoints, "")
			}
		}
	}

	// for this approach of using parallel arrays to part out the configs, we want equal numbers of regions and endpoints
	if len(regions) != len(endpoints) {
		return nil, errors.New("number of regions does not match number of endpoints")
	}

	logger.Info("data", "region", fmt.Sprintf("%+v", regions), "endpoint", fmt.Sprintf("%+v", endpoints))

	for i := 0; i < len(endpoints); i++ {
		if len(regions) > i {
			credsConfig.Region = regions[i]
		} else {
			credsConfig.Region = fallbackRegion
		}
		creds, err := credsConfig.GenerateCredentialChain(opts...)
		if err != nil {
			return nil, err
		}

Review comment (suggestion):
In the IAM case, there should be only one region, but in any case, if no endpoint was supplied, it should remain that way, so add blank strings so the array lengths match.

Code after:

		return configs, nil
	}

	var config rootConfig
	if err := entry.DecodeJSON(&config); err != nil {
		return nil, fmt.Errorf("error reading root configuration: %w", err)
	}

	var endpoints []string
	var regions []string
	credsConfig := &awsutil.CredentialsConfig{}

	credsConfig.AccessKey = config.AccessKey
	credsConfig.SecretKey = config.SecretKey
	credsConfig.HTTPClient = cleanhttp.DefaultClient()
	credsConfig.Logger = logger

	if config.Region != "" {
		regions = append(regions, config.Region)
	}

	maxRetries = config.MaxRetries
	if config.STSEndpoint != "" {
		endpoints = append(endpoints, config.STSEndpoint)
		if config.STSRegion != "" {
			// this retains original logic, where sts region was only used if sts endpoint was set
			regions = []string{config.STSRegion} // override to be "only" region if set
		}

		if len(config.STSFallbackEndpoints) > 0 {
			endpoints = append(endpoints, config.STSFallbackEndpoints...)
		}

		if len(config.STSFallbackRegions) > 0 {
			regions = append(regions, config.STSFallbackRegions...)
		}
	}

	opts := make([]awsutil.Option, 0)
	if config.IdentityTokenAudience != "" {
		ns, err := namespace.FromContext(ctx)
		if err != nil {
			return nil, fmt.Errorf("failed to get namespace from context: %w", err)
		}

		fetcher := &PluginIdentityTokenFetcher{
			sys:      b.System(),
			logger:   b.Logger(),
			ns:       ns,
			audience: config.IdentityTokenAudience,