UNPKG

19.1 kBMarkdownView Raw
1# AWS Identity and Access Management Construct Library
2<!--BEGIN STABILITY BANNER-->
3
4---
5
6![cfn-resources: Stable](https://img.shields.io/badge/cfn--resources-stable-success.svg?style=for-the-badge)
7
8![cdk-constructs: Stable](https://img.shields.io/badge/cdk--constructs-stable-success.svg?style=for-the-badge)
9
10---
11
12<!--END STABILITY BANNER-->
13
14Define a role and add permissions to it. This will automatically create and
15attach an IAM policy to the role:
16
17[attaching permissions to role](test/example.role.lit.ts)
18
19Define a policy and attach it to groups, users and roles. Note that it is possible to attach
20the policy either by calling `xxx.attachInlinePolicy(policy)` or `policy.attachToXxx(xxx)`.
21
22[attaching policies to user and group](test/example.attaching.lit.ts)
23
24Managed policies can be attached using `xxx.addManagedPolicy(ManagedPolicy.fromAwsManagedPolicyName(policyName))`:
25
26[attaching managed policies](test/example.managedpolicy.lit.ts)
27
28## Granting permissions to resources
29
30Many of the AWS CDK resources have `grant*` methods that allow you to grant other resources access to that resource. As an example, the following code gives a Lambda function write permissions (Put, Update, Delete) to a DynamoDB table.
31
32```ts
33declare const fn: lambda.Function;
34declare const table: dynamodb.Table;
35
36table.grantWriteData(fn);
37```
38
39The more generic `grant` method allows you to give specific permissions to a resource:
40
41```ts
42declare const fn: lambda.Function;
43declare const table: dynamodb.Table;
44
45table.grant(fn, 'dynamodb:PutItem');
46```
47
48The `grant*` methods accept an `IGrantable` object. This interface is implemented by IAM principlal resources (groups, users and roles) and resources that assume a role such as a Lambda function, EC2 instance or a Codebuild project.
49
50You can find which `grant*` methods exist for a resource in the [AWS CDK API Reference](https://docs.aws.amazon.com/cdk/api/latest/docs/aws-construct-library.html).
51
52## Roles
53
54Many AWS resources require *Roles* to operate. These Roles define the AWS API
55calls an instance or other AWS service is allowed to make.
56
57Creating Roles and populating them with the right permissions *Statements* is
58a necessary but tedious part of setting up AWS infrastructure. In order to
59help you focus on your business logic, CDK will take care of creating
60roles and populating them with least-privilege permissions automatically.
61
62All constructs that require Roles will create one for you if don't specify
63one at construction time. Permissions will be added to that role
64automatically if you associate the construct with other constructs from the
65AWS Construct Library (for example, if you tell an *AWS CodePipeline* to trigger
66an *AWS Lambda Function*, the Pipeline's Role will automatically get
67`lambda:InvokeFunction` permissions on that particular Lambda Function),
68or if you explicitly grant permissions using `grant` functions (see the
69previous section).
70
71### Opting out of automatic permissions management
72
73You may prefer to manage a Role's permissions yourself instead of having the
74CDK automatically manage them for you. This may happen in one of the
75following cases:
76
77* You don't like the permissions that CDK automatically generates and
78 want to substitute your own set.
79* The least-permissions policy that the CDK generates is becoming too
80 big for IAM to store, and you need to add some wildcards to keep the
81 policy size down.
82
83To prevent constructs from updating your Role's policy, pass the object
84returned by `myRole.withoutPolicyUpdates()` instead of `myRole` itself.
85
86For example, to have an AWS CodePipeline *not* automatically add the required
87permissions to trigger the expected targets, do the following:
88
89```ts
90const role = new iam.Role(this, 'Role', {
91 assumedBy: new iam.ServicePrincipal('codepipeline.amazonaws.com'),
92 // custom description if desired
93 description: 'This is a custom role...',
94});
95
96new codepipeline.Pipeline(this, 'Pipeline', {
97 // Give the Pipeline an immutable view of the Role
98 role: role.withoutPolicyUpdates(),
99});
100
101// You now have to manage the Role policies yourself
102role.addToPolicy(new iam.PolicyStatement({
103 actions: [/* whatever actions you want */],
104 resources: [/* whatever resources you intend to touch */],
105}));
106```
107
108### Using existing roles
109
110If there are Roles in your account that have already been created which you
111would like to use in your CDK application, you can use `Role.fromRoleArn` to
112import them, as follows:
113
114```ts
115const role = iam.Role.fromRoleArn(this, 'Role', 'arn:aws:iam::123456789012:role/MyExistingRole', {
116 // Set 'mutable' to 'false' to use the role as-is and prevent adding new
117 // policies to it. The default is 'true', which means the role may be
118 // modified as part of the deployment.
119 mutable: false,
120});
121```
122
123## Configuring an ExternalId
124
125If you need to create Roles that will be assumed by third parties, it is generally a good idea to [require an `ExternalId`
126to assume them](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-user_externalid.html). Configuring
127an `ExternalId` works like this:
128
129[supplying an external ID](test/example.external-id.lit.ts)
130
131## Principals vs Identities
132
133When we say *Principal*, we mean an entity you grant permissions to. This
134entity can be an AWS Service, a Role, or something more abstract such as "all
135users in this account" or even "all users in this organization". An
136*Identity* is an IAM representing a single IAM entity that can have
137a policy attached, one of `Role`, `User`, or `Group`.
138
139## IAM Principals
140
141When defining policy statements as part of an AssumeRole policy or as part of a
142resource policy, statements would usually refer to a specific IAM principal
143under `Principal`.
144
145IAM principals are modeled as classes that derive from the `iam.PolicyPrincipal`
146abstract class. Principal objects include principal type (string) and value
147(array of string), optional set of conditions and the action that this principal
148requires when it is used in an assume role policy document.
149
150To add a principal to a policy statement you can either use the abstract
151`statement.addPrincipal`, one of the concrete `addXxxPrincipal` methods:
152
153* `addAwsPrincipal`, `addArnPrincipal` or `new ArnPrincipal(arn)` for `{ "AWS": arn }`
154* `addAwsAccountPrincipal` or `new AccountPrincipal(accountId)` for `{ "AWS": account-arn }`
155* `addServicePrincipal` or `new ServicePrincipal(service)` for `{ "Service": service }`
156* `addAccountRootPrincipal` or `new AccountRootPrincipal()` for `{ "AWS": { "Ref: "AWS::AccountId" } }`
157* `addCanonicalUserPrincipal` or `new CanonicalUserPrincipal(id)` for `{ "CanonicalUser": id }`
158* `addFederatedPrincipal` or `new FederatedPrincipal(federated, conditions, assumeAction)` for
159 `{ "Federated": arn }` and a set of optional conditions and the assume role action to use.
160* `addAnyPrincipal` or `new AnyPrincipal` for `{ "AWS": "*" }`
161
162If multiple principals are added to the policy statement, they will be merged together:
163
164```ts
165const statement = new iam.PolicyStatement();
166statement.addServicePrincipal('cloudwatch.amazonaws.com');
167statement.addServicePrincipal('ec2.amazonaws.com');
168statement.addArnPrincipal('arn:aws:boom:boom');
169```
170
171Will result in:
172
173```json
174{
175 "Principal": {
176 "Service": [ "cloudwatch.amazonaws.com", "ec2.amazonaws.com" ],
177 "AWS": "arn:aws:boom:boom"
178 }
179}
180```
181
182The `CompositePrincipal` class can also be used to define complex principals, for example:
183
184```ts
185const role = new iam.Role(this, 'MyRole', {
186 assumedBy: new iam.CompositePrincipal(
187 new iam.ServicePrincipal('ec2.amazonaws.com'),
188 new iam.AccountPrincipal('1818188181818187272')
189 ),
190});
191```
192
193The `PrincipalWithConditions` class can be used to add conditions to a
194principal, especially those that don't take a `conditions` parameter in their
195constructor. The `principal.withConditions()` method can be used to create a
196`PrincipalWithConditions` from an existing principal, for example:
197
198```ts
199const principal = new iam.AccountPrincipal('123456789000')
200 .withConditions({ StringEquals: { foo: "baz" } });
201```
202
203> NOTE: If you need to define an IAM condition that uses a token (such as a
204> deploy-time attribute of another resource) in a JSON map key, use `CfnJson` to
205> render this condition. See [this test](./test/integ.condition-with-ref.ts) for
206> an example.
207
208The `WebIdentityPrincipal` class can be used as a principal for web identities like
209Cognito, Amazon, Google or Facebook, for example:
210
211```ts
212const principal = new iam.WebIdentityPrincipal('cognito-identity.amazonaws.com', {
213 'StringEquals': { 'cognito-identity.amazonaws.com:aud': 'us-east-2:12345678-abcd-abcd-abcd-123456' },
214 'ForAnyValue:StringLike': {'cognito-identity.amazonaws.com:amr': 'unauthenticated' },
215});
216```
217
218If your identity provider is configured to assume a Role with [session
219tags](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html), you
220need to call `.withSessionTags()` to add the required permissions to the Role's
221policy document:
222
223```ts
224new iam.Role(this, 'Role', {
225 assumedBy: new iam.WebIdentityPrincipal('cognito-identity.amazonaws.com', {
226 'StringEquals': {
227 'cognito-identity.amazonaws.com:aud': 'us-east-2:12345678-abcd-abcd-abcd-123456',
228 },
229 'ForAnyValue:StringLike': {
230 'cognito-identity.amazonaws.com:amr': 'unauthenticated',
231 },
232 }).withSessionTags(),
233});
234```
235
236
237## Parsing JSON Policy Documents
238
239The `PolicyDocument.fromJson` and `PolicyStatement.fromJson` static methods can be used to parse JSON objects. For example:
240
241```ts
242const policyDocument = {
243 "Version": "2012-10-17",
244 "Statement": [
245 {
246 "Sid": "FirstStatement",
247 "Effect": "Allow",
248 "Action": ["iam:ChangePassword"],
249 "Resource": "*"
250 },
251 {
252 "Sid": "SecondStatement",
253 "Effect": "Allow",
254 "Action": "s3:ListAllMyBuckets",
255 "Resource": "*"
256 },
257 {
258 "Sid": "ThirdStatement",
259 "Effect": "Allow",
260 "Action": [
261 "s3:List*",
262 "s3:Get*"
263 ],
264 "Resource": [
265 "arn:aws:s3:::confidential-data",
266 "arn:aws:s3:::confidential-data/*"
267 ],
268 "Condition": {"Bool": {"aws:MultiFactorAuthPresent": "true"}}
269 }
270 ]
271};
272
273const customPolicyDocument = iam.PolicyDocument.fromJson(policyDocument);
274
275// You can pass this document as an initial document to a ManagedPolicy
276// or inline Policy.
277const newManagedPolicy = new iam.ManagedPolicy(this, 'MyNewManagedPolicy', {
278 document: customPolicyDocument,
279});
280const newPolicy = new iam.Policy(this, 'MyNewPolicy', {
281 document: customPolicyDocument,
282});
283```
284
285## Permissions Boundaries
286
287[Permissions
288Boundaries](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_boundaries.html)
289can be used as a mechanism to prevent privilege esclation by creating new
290`Role`s. Permissions Boundaries are a Managed Policy, attached to Roles or
291Users, that represent the *maximum* set of permissions they can have. The
292effective set of permissions of a Role (or User) will be the intersection of
293the Identity Policy and the Permissions Boundary attached to the Role (or
294User). Permissions Boundaries are typically created by account
295Administrators, and their use on newly created `Role`s will be enforced by
296IAM policies.
297
298It is possible to attach Permissions Boundaries to all Roles created in a construct
299tree all at once:
300
301```ts
302// This imports an existing policy.
303const boundary = iam.ManagedPolicy.fromManagedPolicyArn(this, 'Boundary', 'arn:aws:iam::123456789012:policy/boundary');
304
305// This creates a new boundary
306const boundary2 = new iam.ManagedPolicy(this, 'Boundary2', {
307 statements: [
308 new iam.PolicyStatement({
309 effect: iam.Effect.DENY,
310 actions: ['iam:*'],
311 resources: ['*'],
312 }),
313 ],
314});
315
316// Directly apply the boundary to a Role you create
317declare const role: iam.Role;
318iam.PermissionsBoundary.of(role).apply(boundary);
319
320// Apply the boundary to an Role that was implicitly created for you
321declare const fn: lambda.Function;
322iam.PermissionsBoundary.of(fn).apply(boundary);
323
324// Apply the boundary to all Roles in a stack
325iam.PermissionsBoundary.of(this).apply(boundary);
326
327// Remove a Permissions Boundary that is inherited, for example from the Stack level
328declare const customResource: CustomResource;
329iam.PermissionsBoundary.of(customResource).clear();
330```
331
332## OpenID Connect Providers
333
334OIDC identity providers are entities in IAM that describe an external identity
335provider (IdP) service that supports the [OpenID Connect] (OIDC) standard, such
336as Google or Salesforce. You use an IAM OIDC identity provider when you want to
337establish trust between an OIDC-compatible IdP and your AWS account. This is
338useful when creating a mobile app or web application that requires access to AWS
339resources, but you don't want to create custom sign-in code or manage your own
340user identities. For more information about this scenario, see [About Web
341Identity Federation] and the relevant documentation in the [Amazon Cognito
342Identity Pools Developer Guide].
343
344[OpenID Connect]: http://openid.net/connect
345[About Web Identity Federation]: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_oidc.html
346[Amazon Cognito Identity Pools Developer Guide]: https://docs.aws.amazon.com/cognito/latest/developerguide/open-id.html
347
348The following examples defines an OpenID Connect provider. Two client IDs
349(audiences) are will be able to send authentication requests to
350<https://openid/connect>.
351
352```ts
353const provider = new iam.OpenIdConnectProvider(this, 'MyProvider', {
354 url: 'https://openid/connect',
355 clientIds: [ 'myclient1', 'myclient2' ],
356});
357```
358
359You can specify an optional list of `thumbprints`. If not specified, the
360thumbprint of the root certificate authority (CA) will automatically be obtained
361from the host as described
362[here](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc_verify-thumbprint.html).
363
364Once you define an OpenID connect provider, you can use it with AWS services
365that expect an IAM OIDC provider. For example, when you define an [Amazon
366Cognito identity
367pool](https://docs.aws.amazon.com/cognito/latest/developerguide/open-id.html)
368you can reference the provider's ARN as follows:
369
370```ts
371import * as cognito from '@aws-cdk/aws-cognito';
372
373declare const myProvider: iam.OpenIdConnectProvider;
374new cognito.CfnIdentityPool(this, 'IdentityPool', {
375 openIdConnectProviderArns: [myProvider.openIdConnectProviderArn],
376 // And the other properties for your identity pool
377 allowUnauthenticatedIdentities: false,
378});
379```
380
381The `OpenIdConnectPrincipal` class can be used as a principal used with a `OpenIdConnectProvider`, for example:
382
383```ts
384const provider = new iam.OpenIdConnectProvider(this, 'MyProvider', {
385 url: 'https://openid/connect',
386 clientIds: [ 'myclient1', 'myclient2' ],
387});
388const principal = new iam.OpenIdConnectPrincipal(provider);
389```
390
391## SAML provider
392
393An IAM SAML 2.0 identity provider is an entity in IAM that describes an external
394identity provider (IdP) service that supports the SAML 2.0 (Security Assertion
395Markup Language 2.0) standard. You use an IAM identity provider when you want
396to establish trust between a SAML-compatible IdP such as Shibboleth or Active
397Directory Federation Services and AWS, so that users in your organization can
398access AWS resources. IAM SAML identity providers are used as principals in an
399IAM trust policy.
400
401```ts
402new iam.SamlProvider(this, 'Provider', {
403 metadataDocument: iam.SamlMetadataDocument.fromFile('/path/to/saml-metadata-document.xml'),
404});
405```
406
407The `SamlPrincipal` class can be used as a principal with a `SamlProvider`:
408
409```ts
410const provider = new iam.SamlProvider(this, 'Provider', {
411 metadataDocument: iam.SamlMetadataDocument.fromFile('/path/to/saml-metadata-document.xml'),
412});
413const principal = new iam.SamlPrincipal(provider, {
414 StringEquals: {
415 'SAML:iss': 'issuer',
416 },
417});
418```
419
420When creating a role for programmatic and AWS Management Console access, use the `SamlConsolePrincipal`
421class:
422
423```ts
424const provider = new iam.SamlProvider(this, 'Provider', {
425 metadataDocument: iam.SamlMetadataDocument.fromFile('/path/to/saml-metadata-document.xml'),
426});
427new iam.Role(this, 'Role', {
428 assumedBy: new iam.SamlConsolePrincipal(provider),
429});
430```
431
432## Users
433
434IAM manages users for your AWS account. To create a new user:
435
436```ts
437const user = new iam.User(this, 'MyUser');
438```
439
440To import an existing user by name [with path](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_identifiers.html#identifiers-friendly-names):
441
442```ts
443const user = iam.User.fromUserName(this, 'MyImportedUserByName', 'johnsmith');
444```
445
446To import an existing user by ARN:
447
448```ts
449const user = iam.User.fromUserArn(this, 'MyImportedUserByArn', 'arn:aws:iam::123456789012:user/johnsmith');
450```
451
452To import an existing user by attributes:
453
454```ts
455const user = iam.User.fromUserAttributes(this, 'MyImportedUserByAttributes', {
456 userArn: 'arn:aws:iam::123456789012:user/johnsmith',
457});
458```
459
460### Access Keys
461
462The ability for a user to make API calls via the CLI or an SDK is enabled by the user having an
463access key pair. To create an access key:
464
465```ts
466const user = new iam.User(this, 'MyUser');
467const accessKey = new iam.AccessKey(this, 'MyAccessKey', { user: user });
468```
469
470You can force CloudFormation to rotate the access key by providing a monotonically increasing `serial`
471property. Simply provide a higher serial value than any number used previously:
472
473```ts
474const user = new iam.User(this, 'MyUser');
475const accessKey = new iam.AccessKey(this, 'MyAccessKey', { user: user, serial: 1 });
476```
477
478An access key may only be associated with a single user and cannot be "moved" between users. Changing
479the user associated with an access key replaces the access key (and its ID and secret value).
480
481## Groups
482
483An IAM user group is a collection of IAM users. User groups let you specify permissions for multiple users.
484
485```ts
486const group = new iam.Group(this, 'MyGroup');
487```
488
489To import an existing group by ARN:
490
491```ts
492const group = iam.Group.fromGroupArn(this, 'MyImportedGroupByArn', 'arn:aws:iam::account-id:group/group-name');
493```
494
495To import an existing group by name [with path](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_identifiers.html#identifiers-friendly-names):
496
497```ts
498const group = iam.Group.fromGroupName(this, 'MyImportedGroupByName', 'group-name');
499```
500
501To add a user to a group (both for a new and imported user/group):
502
503```ts
504const user = new iam.User(this, 'MyUser'); // or User.fromUserName(stack, 'User', 'johnsmith');
505const group = new iam.Group(this, 'MyGroup'); // or Group.fromGroupArn(stack, 'Group', 'arn:aws:iam::account-id:group/group-name');
506
507user.addToGroup(group);
508// or
509group.addUser(user);
510```
511
512## Features
513
514* Policy name uniqueness is enforced. If two policies by the same name are attached to the same
515 principal, the attachment will fail.
516* Policy names are not required - the CDK logical ID will be used and ensured to be unique.
517* Policies are validated during synthesis to ensure that they have actions, and that policies
518 attached to IAM principals specify relevant resources, while policies attached to resources
519 specify which IAM principals they apply to.