chore(pulumi/components): add standardized aws service account example

This commit is contained in:
Michele Cereda
2025-04-16 18:23:56 +02:00
parent c6198f2743
commit a04f251685
4 changed files with 174 additions and 24 deletions

View File

@@ -65,13 +65,15 @@ Refer [aws.permissions.cloud] for a community-driven source of truth for AWS IAM
## Users
Refer [IAM users].
Refer [IAM Users].
Represent a human user or workload needing to interact with AWS resources.<br/>
Represent human users or workloads needing to interact with AWS resources.<br/>
Consist of a name and credentials.<br/>
Applications using their credentials to make requests are typically referred to as _service accounts_.
IAM Users with administrator permissions are **not** the same thing as the AWS account's root user.
IAM Users with administrator permissions are **not** the same thing as the AWS Account's root user.<br/>
The root user is **required** to perform some [specific tasks][tasks that require root user credentials] on the account,
which will **not** be available if signed in as any other user.
IAM identifies IAM Users via:
@@ -140,7 +142,16 @@ Their _effect_ can be to `allow` or `deny` such actions. A `deny` statement **al
Mostly stored as structured JSON documents.<br/>
Each Policy comes with one or several _statements_. Each statement defines an effect.
IAM does not expose Policies' `Sid` element in the IAM API, so it can't be used to retrieve statements.
IAM does **not** expose Policies' `Sid` element in the IAM API, so it **cannot** be used to filter retrieved statements.
Logical evaluation:
- **Statements** in a Policy operate in an `OR` fashion.<br/>
As in, **at least one** statement must allow access to a set of resources.
- **Conditions** in a Statement operate in an `AND` fashion.<br/>
As in, **all** conditions must resolve true for the statement to allow access.
- **Operator values** in a Condition operate in an `OR` fashion.<br/>
As in, **at least one** value must match for a Condition to resolve true.
Policy examples:
@@ -202,7 +213,7 @@ Policy examples:
### Trust Policies
Specific type of resource-based policy for IAM roles.<br/>
Used to allow Principals ans AWS Services to assume Roles.
Used to allow Principals and other AWS Services to assume Roles.
### Trust Relationships
@@ -245,7 +256,6 @@ Principals and AWS Services can assume Roles as long as:
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowMeToAssumeThoseRoles",
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": [
@@ -287,7 +297,7 @@ Principals and AWS Services can assume Roles as long as:
Allowed entities can assume Roles using the [STS AssumeRole API][assumerole api reference].
<details style="margin-top: -1em; padding-bottom: 1em;">
<details style="margin: -1rem 0 1rem 1rem">
```sh
$ aws sts assume-role --role-arn "arn:aws:iam::012345678901:role/EksAdminRole" \
@@ -316,7 +326,7 @@ Refer [Using AWS CLI Securely with IAM Roles and MFA].
Add the `"Bool": {"aws:MultiFactorAuthPresent": true}` condition to the Role's Trust Relationships.
<details style="margin-top: -1em; padding-bottom: 1em;">
<details style="margin: -1rem 0 1rem 1rem">
```json
{
@@ -338,13 +348,32 @@ Add the `"Bool": {"aws:MultiFactorAuthPresent": true}` condition to the Role's T
</details>
When requiring MFA with AssumeRole, identities need to pass values for the SerialNumber and TokenCode parameters.<br/>
SerialNumbers identify the users' hardware or virtual MFA devices, TokenCodes are the time-based one-time password
(TOTP) value that devices produce.
When requiring MFA with AssumeRole, identities **must** pass values for the `SerialNumber` and `TokenCode`
parameters.<br/>
`SerialNumber`s identify the users' hardware or virtual MFA devices, while `TokenCode`s are the time-based one-time
password (TOTP) value that devices produce.
For CLI access, the user will need to add the `mfa_serial` setting to their profile.
<details style="margin: -1rem 0 1rem 1rem">
<details style="margin-top: -1em; padding-bottom: 1em;">
```sh
$ aws sts assume-role --output 'yaml' --duration-seconds '900' \
--role-arn 'arn:aws:iam::012345678901:role/EksAdminRole' --role-session-name 'lookAt-him-heIsThe-EksAdmin-now' \
--serial-number 'arn:aws:iam::012345678901:mfa/gopass' --token-code '123456'
AssumedRoleUser:
Arn: arn:aws:sts::012345678901:assumed-role/EksAdminRole/lookAt-him-heIsThe-EksAdmin-now
AssumedRoleId: AROA2HKHF74L72AABBCCDD:lookAt-him-heIsThe-EksAdmin-now
Credentials:
AccessKeyId: ASIA2HKHF74L7YOAUZHR
Expiration: '2025-04-12T08:09:46+00:00'
SecretAccessKey: ErhyPKjQkI3GbrnszpOvMTi8AvmziGbSIOIcNS9k
SessionToken: IQoJb3JpZ2…LxEOLkm9U
```
</details>
For CLI access, users will **need** to add the `mfa_serial` setting to their profile.
<details style="margin: -1rem 0 1rem 1rem">
```ini
[default]
@@ -372,6 +401,7 @@ UserId: AROA2HKHF74L72AABBCCDD:botocore-session-1234567890
- [aws.permissions.cloud]
- [Using service-linked roles]
- [IAM and AWS STS quotas]
- [AWS global condition context keys]
### Sources
@@ -412,6 +442,7 @@ UserId: AROA2HKHF74L72AABBCCDD:botocore-session-1234567890
<!-- Files -->
<!-- Upstream -->
[assumerole api reference]: https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html
[aws global condition context keys]: https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_condition-keys.html
[can i increase the duration of the iam role chaining session?]: https://repost.aws/knowledge-center/iam-role-chaining-limit
[creating a role to delegate permissions to an iam user]: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-user.html
[how can i monitor the account activity of specific iam users, roles, and aws access keys?]: https://repost.aws/knowledge-center/view-iam-history
@@ -423,6 +454,7 @@ UserId: AROA2HKHF74L72AABBCCDD:botocore-session-1234567890
[iam user groups]: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_groups.html
[iam users]: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users.html
[not authorized to perform: sts:assumerole]: https://repost.aws/questions/QUOY5XngCtRyOX4Desaygz8Q/not-authorized-to-perform-sts-assumerole
[tasks that require root user credentials]: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_root-user.html#root-user-tasks
[troubleshooting iam roles]: https://docs.aws.amazon.com/IAM/latest/UserGuide/troubleshoot_roles.html
[use an iam role in the aws cli]: https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-role.html
[using iam policy conditions for fine-grained access control to manage resource record sets]: https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/specifying-rrset-conditions.html

View File

@@ -857,7 +857,7 @@ backend:
FIXME: should this be under [Program]?
Refer [Component resources].
Refer [Component resources] and [Create a ComponentResource].
Logical grouping of resources.<br/>
Usually leveraged to instantiate a set of related resources, aggregate them as children, and create larger abstractions
@@ -882,6 +882,19 @@ Refer [Pulumi Crosswalk for AWS] or [Google Cloud Static Website] as examples.
</details>
1. Declare the types of resources one wants to export from the class.
<details style="padding: 0 0 1rem 1rem">
```ts
class StandardAwsVpc extends pulumi.ComponentResource {
internetGateway: aws.ec2.InternetGateway
vpc: aws.ec2.Vpc
};
```
</details>
1. Inside its constructor, chain to the base constructor and pass it the subclass' name, arguments, and options.
Upon creation of a new instance of the Component, the call to the base constructor registers the instance with the
@@ -892,10 +905,12 @@ Refer [Pulumi Crosswalk for AWS] or [Google Cloud Static Website] as examples.
Components must also register a unique _type name_ with the base constructor. These names are namespaced alongside
non-Component resources such as `aws:lambda:Function`.
<details style="padding: 0 0 1em 1em">
<details style="padding: 0 0 1rem 1rem">
```ts
class StandardAwsVpc extends pulumi.ComponentResource {
constructor(name: string, args: pulumi.Inputs, opts?: pulumi.ComponentResourceOptions) {
super("exampleOrg:StandardAwsVpc", name, {}, opts);
};
@@ -911,15 +926,17 @@ Refer [Pulumi Crosswalk for AWS] or [Google Cloud Static Website] as examples.
```ts
class StandardAwsVpc extends pulumi.ComponentResource {
constructor(name: string, args: pulumi.Inputs, opts?: pulumi.ComponentResourceOptions) {
const vpc = new aws.ec2.Vpc(
this.vpc = new aws.ec2.Vpc(
`${name}`,
{ … },
{ parent: this },
);
const internetGateway = new aws.ec2.InternetGateway(
this.internetGateway = new aws.ec2.InternetGateway(
`${name}`,
{
vpcId: vpc.id,
@@ -946,7 +963,7 @@ Refer [Pulumi Crosswalk for AWS] or [Google Cloud Static Website] as examples.
this.registerOutputs({
vpcId: vpc.id,
vpcId: this.vpc.id,
});
};
};
@@ -972,16 +989,21 @@ Refer [Pulumi Crosswalk for AWS] or [Google Cloud Static Website] as examples.
</details>
<details>
<summary>Sample code</summary>
<summary>Example: standardized AWS VPC</summary>
FIXME: check
```ts
import * as aws from "@pulumi/aws";
export class StandardAwsVpc extends pulumi.ComponentResource {
internetGateway: aws.ec2.InternetGateway
vpc: aws.ec2.Vpc
constructor(name: string, args: pulumi.Inputs, opts?: pulumi.ComponentResourceOptions) {
super("exampleOrg:StandardAwsVpc", name, {}, opts);
const vpc = new aws.ec2.Vpc(
this.vpc = new aws.ec2.Vpc(
`${name}`,
{
tags: {
@@ -994,7 +1016,7 @@ export class StandardAwsVpc extends pulumi.ComponentResource {
},
{ parent: this },
);
const internetGateway = new aws.ec2.InternetGateway(
this.internetGateway = new aws.ec2.InternetGateway(
name,
{
tags: {
@@ -1002,14 +1024,14 @@ export class StandardAwsVpc extends pulumi.ComponentResource {
...args.tags,
},
vpcId: vpc.id,
vpcId: this.vpc.id,
},
{ parent: vpc },
{ parent: this.vpc },
);
this.registerOutputs({
vpcId: vpc.id,
vpcId: this.vpc.id,
});
};
};
@@ -1029,6 +1051,93 @@ const currentVpc = new StandardAwsVpc(
</details>
<details>
<summary>Example: standardized AWS service role</summary>
```ts
import * as pulumi from '@pulumi/pulumi';
import * as aws from '@pulumi/aws';
class StandardServiceRole extends pulumi.ComponentResource {
assumeRole: {
iamPolicy: aws.iam.Policy
}
iamRole: aws.iam.Role
constructor(
name: string,
args: aws.iam.RoleArgs,
opts?: pulumi.ComponentResourceOptions,
) {
super('exampleOrg:StandardServiceRole', name, {}, opts);
this.iamRole = new aws.iam.Role(
name,
{
...args,
tags: {
ServiceRole: 'true',
StandardResource: 'true',
...args.tags,
},
},
{ parent: this },
);
this.assumeRole = {
iamPolicy: new aws.iam.Policy(
`${name}-assumeRole`,
{
name: `${args.name}-AssumeRole`,
description: `Allows bearers to try and assume the ${args.name} standard service role`,
tags: {
StandardResource: 'true',
},
policy: pulumi.jsonStringify({
Version: '2012-10-17',
Statement: [{
Sid: `AllowAssumingThe${args.name}Role`,
Effect: 'Allow',
Action: 'sts:AssumeRole',
Resource: this.iamRole.arn,
}],
}),
},
{ parent: this },
),
};
this.registerOutputs({
iamRole: this.iamRole,
assumeRole: this.assumeRole,
});
};
};
const serviceRole = new StandardServiceRole(
'someServiceRole',
{
name: 'SomeServiceRole',
description: 'SomeServiceRole',
assumeRolePolicy: pulumi.jsonStringify({
Version: '2012-10-17',
Statement: [{
Effect: 'Allow',
Principal: {
AWS: '012345678901',
},
Action: 'sts:AssumeRole',
}],
}),
},
);
serviceRole.assumeRole.iamPolicy.name.apply(policyName => console.log(policyName));
```
</details>
## Import resources
FIXME: should this be under [Program] or [Stack]?
@@ -1297,6 +1406,7 @@ Solution: follow the suggestion in the warning message:
- [`pulumi config set-all`][pulumi config set-all]
- [Importing resources]
- [Property paths]
- [Create a ComponentResource]
<!--
Reference
@@ -1351,5 +1461,6 @@ Solution: follow the suggestion in the warning message:
<!-- Others -->
[assigning tags by default on aws with pulumi]: https://blog.scottlowe.org/2023/09/11/assigning-tags-by-default-on-aws-with-pulumi/
[create a componentresource]: https://pulumi.awsworkshop.io/30_modern_iac_ts/45_componens/10_create_component.html
[docker images]: https://hub.docker.com/u/pulumi
[things i wish i knew earlier about pulumi]: https://vsupalov.com/pulumi-learnings/

View File

@@ -23,6 +23,9 @@ aws --profile 'engineer' sts assume-role \
aws sts get-caller-identity
AWS_PROFILE='engineer' aws sts get-caller-identity
# Clear cached credentials
rm -r ~'/.aws/cli/cache'
# Run as Docker container
docker run --rm -ti 'amazon/aws-cli' --version

View File

@@ -38,6 +38,7 @@ pulumi config set 'boincAcctMgrUrl' 'https://bam.boincstats.com'
pulumi config set --secret 'boincGuiRpcPasswd' 'something-something-darkside'
pulumi config set --path 'outer.inner' 'value'
pulumi config set --path 'list[1]' 'value'
gpg --export 'smth@example.org' | pulumi config set 'smthTeam:pgpKey-public-raw' --type 'string'
# Gitlab provider
# 'baseUrl' requires the ending slash
@@ -78,6 +79,9 @@ pulumi stack export | jq -r '.deployment.resources[]|{"urn":.urn,"provider":.pro
# Check providers are all of a specific version
pulumi stack export | jq -r '.deployment.resources[].provider' | grep -v 'aws::default_6_50_0'
# Get the AWS secret access key of an aws.iam.AccessKey resource
pulumi stack output 'someAccessKey' | jq -r '.encryptedSecret' - | base64 -d | gpg --decrypt
# Avoid permission errors when deleting clusters with charts and stuff.
PULUMI_K8S_DELETE_UNREACHABLE='true' pulumi destroy